Erro E0005 do Rust: refutable pattern in local binding (let que pode falhar)
O E0005 quer dizer que você escreveu um let cujo padrão pode não casar, como let Some(x) = value; quando value pode ser None. O Rust precisa saber o que acontece nesse caso, e um let simples não tem onde dizer isso. A correção é dar um destino para o caso “não casou”: normalmente let ... else, if let ou match. É um erro muito comum quando você começa a trabalhar com Option e Result, e leva um minuto para corrigir. Para os outros erros desta série, veja Erros do compilador Rust.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0005]: refutable pattern in local binding --> src/main.rs:4:9 |4 | let Some(top) = best; // ❌ e se a lista estivesse vazia? | ^^^^^^^^^ pattern `None` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html = note: the matched value is of type `Option<i32>`help: you might want to use `let...else` to handle the variant that isn't matched |4 | let Some(top) = best else { todo!() }; // ❌ e se a lista estivesse vazia? | ++++++++++++++++Em português: “padrão refutável numa declaração local”, ou seja, um padrão que pode falhar dentro de um let. O rustc avisa que o caso None não está coberto, lembra que o let exige um padrão que sempre casa e sugere usar let...else para tratar o caso que falta.
Este é o programa que gera o erro:
fn main() { let scores = vec![72, 95, 88]; let best = scores.iter().max().copied(); let Some(top) = best; // ❌ e se a lista estivesse vazia? println!("Top score: {top}");}Você vai ver o mesmo erro com outros padrões. Só muda a parte “not covered” (não coberto):
3 | let Ok(age) = input.parse::<i32>(); | ^^^^^^^ pattern `Err(_)` not covered
3 | let [host, port] = parts.as_slice(); | ^^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _, ..]` not coveredNo primeiro, falta o caso Err(_). No segundo, faltam a slice vazia, a de um item e a de três ou mais itens.
O que significa
Seção intitulada “O que significa”Duas palavras fazem todo o trabalho aqui:
- Um padrão refutável pode falhar.
Some(top)falha quando o valor éNone.Ok(age)falha comErr.[host, port]falha quando a slice não tem exatamente dois itens. - Um padrão irrefutável sempre casa. Um nome simples, como em
let top = best;, sempre funciona, e tuplas e structs também, como emlet (x, y) = point;.
Um let precisa sempre dar certo, porque a linha seguinte usa as variáveis que ele cria. Se best fosse None, o que seria top na linha seguinte? Não há resposta, então o Rust para você. O rótulo “not covered” lista exatamente os casos que você esqueceu: None, Err(_) ou, para a slice, “vazia”, “um item” e “três ou mais itens”.
Pense num armário de retirada de encomendas. let Some(top) = best; é como ir embora certo de que tem um pacote lá dentro, sem abrir a porta. O Rust faz você decidir o que fazer se o armário estiver vazio.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”O let, os parâmetros de funções e os laços for precisam de padrões que não podem falhar. match, if let, while let e let ... else aceitam padrões que podem falhar, porque cada um deles tem um lugar para o outro caso. É a mesma regra do E0004, em que um match precisa cobrir todos os valores. Aqui o “match” é um let com um braço só.
Em muitas linguagens, ler um valor que não existe dá null, undefined ou uma falha em tempo de execução. Em Rust, uma lista vazia, um número inválido num campo de texto ou uma configuração que falta são casos normais, que você trata no código. Option e Result deixam esses casos visíveis, e o E0005 garante que você não pule nenhum sem querer. Veja Padrões no let e Option.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: let ... else para sair mais cedo
Seção intitulada “Correção 1: let ... else para sair mais cedo”Escolha esta quando o resto da função precisa do valor e, sem ele, não há nada de útil para fazer. É o que o help: do rustc sugere. Você mantém o seu padrão e acrescenta um bloco else. Esse bloco precisa sair: return, break, continue ou um panic.
fn port_from(text: &str) -> u16 { // tira o número de dentro, ou sai da função mais cedo let Ok(port) = text.trim().parse::<u16>() else { println!("'{text}' não é uma porta válida, usando 8080"); return 8080; }; port}
fn main() { let scores = vec![72, 95, 88]; let Some(top) = scores.iter().max() else { println!("Ainda não há notas"); return; }; println!("Maior nota: {top}");
println!("Porta: {}", port_from("3000")); println!("Porta: {}", port_from("abc"));}Saída:
Maior nota: 95Porta: 3000'abc' não é uma porta válida, usando 8080Porta: 8080Não deixe o else { todo!() } do rustc do jeito que está. O todo!() dá panic quando roda, então troque por uma saída de verdade. Mais em let-else.
Correção 2: match ou if let quando os dois casos fazem algo
Seção intitulada “Correção 2: match ou if let quando os dois casos fazem algo”Escolha esta quando o caso “não casou” tem o próprio trabalho a fazer, como mostrar o erro, ou quando o código continua de qualquer jeito. O match deixa você tratar todos os casos e usar o valor do erro. Ele também é o jeito de desmontar uma slice quando você não sabe o tamanho dela. Use if let quando só um caso importa:
fn main() { // match: trata os dois resultados e usa o erro for input in ["42", "quarenta e dois"] { match input.parse::<i32>() { Ok(age) => println!("Idade: {age}"), Err(e) => println!("Não consegui ler '{input}': {e}"), } }
// uma slice pode ter qualquer tamanho: faça match no formato dela for address in ["localhost:8080", "localhost"] { let parts: Vec<&str> = address.split(':').collect(); match parts.as_slice() { [host, port] => println!("host = {host}, porta = {port}"), _ => println!("'{address}' deveria ter o formato host:porta"), } }
// if let: só faz algo quando existe um valor let nickname: Option<&str> = None; if let Some(name) = nickname { println!("Oi, {name}!"); } else { println!("Oi, pessoa desconhecida!"); }}Saída:
Idade: 42Não consegui ler 'quarenta e dois': invalid digit found in stringhost = localhost, porta = 8080'localhost' deveria ter o formato host:portaOi, pessoa desconhecida!invalid digit found in string (dígito inválido encontrado na string) é a mensagem do próprio Rust, por isso fica em inglês. O braço _ pega todos os outros tamanhos. Sem ele, o match daria o E0004, com a mesma lista de “not covered”. Veja if let e match.
Correção 3: Um valor padrão com unwrap_or, ou repasse com ?
Seção intitulada “Correção 3: Um valor padrão com unwrap_or, ou repasse com ?”Escolha esta quando existe um valor padrão que faz sentido, ou quando quem chamou a função deve lidar com o problema. unwrap_or(x) entrega o valor, ou x se não houver nenhum. O ? funciona dentro de uma função que retorna Result (ou Option): ele devolve o erro para quem chamou e encerra a função.
use std::num::ParseIntError;
// ? repassa o erro para quem chamou, em vez de tratá-lo aquifn total_minutes(hours: &str, minutes: &str) -> Result<i32, ParseIntError> { let h = hours.parse::<i32>()?; let m = minutes.parse::<i32>()?; Ok(h * 60 + m)}
fn main() { // um valor padrão quando não há nada let scores: Vec<i32> = Vec::new(); let top = scores.iter().max().copied().unwrap_or(0); println!("Maior nota: {top}");
let volume = "alto".parse::<u8>().unwrap_or(50); println!("Volume: {volume}");
println!("{:?}", total_minutes("2", "30")); println!("{:?}", total_minutes("2", "meia"));}Saída:
Maior nota: 0Volume: 50Ok(150)Err(ParseIntError { kind: InvalidDigit })Se você tem certeza de que o valor existe (por exemplo, um número que você mesmo escreveu no código), .expect("motivo") também serve. Ele dá panic com a sua mensagem se você estiver errado, então guarde-o para casos que realmente não podem falhar. Usar ? num main que não retorna nada dá o E0277; veja O operador ? e Atalhos: unwrap_or.
Correção 4: Use um tipo cujo padrão não pode falhar
Seção intitulada “Correção 4: Use um tipo cujo padrão não pode falhar”Escolha esta quando os dados sempre têm o mesmo formato, e o padrão só falha por causa do tipo que você escolheu. Uma slice ou um Vec pode ter qualquer tamanho, mas um array como [u8; 3] sempre tem exatamente três itens, então let [r, g, b] = color; sempre casa. Tuplas e structs também sempre casam. E o split_once entrega as duas metades numa tupla só, então você lida com um único Option em vez de uma slice de tamanho desconhecido:
struct Point { x: i32, y: i32,}
fn main() { // arrays têm tamanho fixo: este padrão sempre casa let color: [u8; 3] = [255, 140, 0]; let [r, g, b] = color; println!("r={r} g={g} b={b}");
// tuplas e structs também sempre casam let (host, port) = ("localhost", 8080); let Point { x, y } = Point { x: 3, y: -1 }; println!("{host}:{port} em ({x}, {y})");
// split_once retorna um Option só, com as duas metades let Some((key, value)) = "theme=dark".split_once('=') else { return; }; println!("{key} -> {value}");}Saída:
r=255 g=140 b=0localhost:8080 em (3, -1)theme -> darkO mesmo vale para os seus próprios enums: let Shape::Circle(radius) = shape; falha com pattern `Shape::Square(_)` not covered. Se o valor realmente só pode ser um círculo, talvez ele devesse ser uma struct Circle em vez de um enum. Veja Arrays e Structs.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Um laço for sobre Options. for Some(temp) in readings dá “refutable pattern in for loop binding” (padrão refutável na variável do laço for). Correção: for temp in readings.iter().flatten() pula os None, ou percorra todos os itens e use if let Some(t) = reading lá dentro.
Um parâmetro de função. fn greet(Some(name): Option<&str>) dá “refutable pattern in function argument” (padrão refutável no argumento da função). Correção: receba name: Option<&str> e faça um match dentro da função, ou receba name: &str e deixe quem chama decidir o que fazer com None.
Ler uma entrada. let Ok(n) = line.trim().parse::<i32>(); ao ler o que o usuário digitou ou argumentos de linha de comando. Correção: let Ok(n) = ... else { println!("Digite um número"); continue; }; dentro do laço de entrada, ou um match para mostrar o erro.
Erros relacionados
Seção intitulada “Erros relacionados”- E0004: non-exhaustive patterns: a mesma verificação de “not covered”, para
matchem vez delet. - E0308: mismatched types: o padrão nem combina com o tipo, por exemplo
let Some(x) = 5;. - E0277: trait bound not satisfied: o que você recebe ao usar
?numa função que não retornaResultnemOption, como umfn main()simples. - E0282: type annotations needed: costuma aparecer perto do
parse(), quando o Rust não sabe que tipo você quer. Acrescente::<i32>, como nos exemplos acima. - Todos os erros do compilador: a lista completa de páginas de erros.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Qual let dá o E0005?
2. O que o bloco else de um let ... else precisa fazer?
3. Você precisa de um número a partir de um texto, e 0 é um bom valor padrão se o texto não for um número. Qual é a correção mais curta?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0005 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Refutability — The Rust Book (em inglês): o capítulo para o qual o rustc aponta, sobre padrões que podem e que não podem falhar.
- All the Places Patterns Can Be Used — The Rust Book (em inglês):
let,if let,while let, laçosfore parâmetros de funções. - let-else — Rust by Example (em inglês): exemplos curtos e executáveis de
let ... else. - if let — Rust by Example (em inglês): quando o
if letfica mais legível que ummatchcompleto.