Erro E0631 do Rust: type mismatch in closure arguments (tipo errado na closure)
O E0631 quer dizer que a sua closure (função anônima) tem a quantidade certa de parâmetros, mas pelo menos um deles tem o tipo errado. Em nove de cada dez casos, a diferença é um único &: você escreveu |x: i32|, mas o iterador empresta um &i32. A correção mais rápida costuma ser apagar a anotação de tipo e deixar o Rust descobrir sozinho. Esta página mostra essa correção primeiro e depois explica de onde vem o &, para você conseguir prever na próxima vez. Para os outros erros desta série, veja Erros do compilador Rust.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0631]: type mismatch in closure arguments --> src/main.rs:3:43 |3 | let doubled: Vec<i32> = prices.iter().map(|p: i32| p * 2).collect(); // ❌ os itens são &i32 | ^^^ -------- found signature defined here | | | expected due to this | = note: expected closure signature `fn(&{integer}) -> _` found closure signature `fn(i32) -> _`note: required by a bound in `map`help: consider adjusting the signature so it borrows its argument |3 | let doubled: Vec<i32> = prices.iter().map(|p: &i32| p * 2).collect(); // ❌ os itens são &i32 | +Em português: “tipos incompatíveis nos argumentos da closure”. O rustc mostra a assinatura que o map esperava e a que encontrou, e o help: sugere fazer a closure pegar o argumento emprestado, acrescentando um &.
Este é o programa que gera o erro:
fn main() { let prices = vec![120, 45, 300]; let doubled: Vec<i32> = prices.iter().map(|p: i32| p * 2).collect(); // ❌ os itens são &i32 println!("{:?}", doubled);}Logo depois, o rustc mostra um segundo erro, mais longo: the method `collect` exists for struct `Map<...>`, but its trait bounds were not satisfied (E0599). Esse é só um efeito colateral. Corrija o E0631 e ele desaparece.
O que significa
Seção intitulada “O que significa”As duas linhas note são o coração da mensagem. Leia como “o método queria isto, você escreveu aquilo”:
- expected
fn(&{integer}) -> _: omapvai chamar a sua closure com uma referência para um inteiro.{integer}quer dizer “algum tipo inteiro que o Rust ainda não definiu”, e_quer dizer “qualquer tipo de retorno”. - found
fn(i32) -> _: a sua closure diz que recebe umi32por valor.
Coloque as duas lado a lado e a única diferença é o &. A linha help: mostra a correção de um caractere.
Pense num guarda-volumes. Você entrega a sua mala e recebe um tíquete. O .iter() funciona do mesmo jeito: ele não entrega os itens, entrega tíquetes para eles (referências). Uma closure que insiste em receber a própria mala não aceita um tíquete.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Todo método de iterador decide exatamente que tipo vai passar para a sua closure, e esse tipo depende de como você criou o iterador:
v.iter()empresta cada item:&T.v.into_iter()(numVecque é seu) entrega cada item de vez:T.filterefindemprestam o item de novo, então em cima de.iter()você recebe&&T.sort_byempresta dois itens:&Te&T.
Quando você não escreve os tipos dos parâmetros, o Rust os preenche a partir do método, então eles sempre encaixam. Quando você escreve um tipo, o Rust acredita em você e confere. Ele não copia o valor escondido nem transforma uma referência num valor próprio, porque isso mudaria o que o seu código faz (e, numa String, uma cópia custa memória e tempo). A lição Iteradores mostra as três formas de obter um iterador, e Borrowing explica as referências e o borrowing (empréstimo).
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Tire a anotação de tipo
Seção intitulada “Correção 1: Tire a anotação de tipo”Escolha esta primeiro. Parâmetros de closure quase nunca precisam de tipo: o Rust sabe exatamente o que map, filter e sort_by passam. Apague o : i32 e o código compila:
fn main() { let prices = vec![120, 45, 300]; let ages = vec![12, 34, 17, 50]; let mut scores = vec![42, 7, 99, 15];
// sem anotações de tipo: o Rust usa o que o iterador realmente entrega let doubled: Vec<i32> = prices.iter().map(|p| p * 2).collect(); let adults: Vec<&i32> = ages.iter().filter(|age| **age >= 18).collect(); scores.sort_by(|a, b| b.cmp(a));
println!("{:?}", doubled); println!("{:?}", adults); println!("{:?}", scores);}Saída:
[240, 90, 600][34, 50][99, 42, 15, 7]p * 2 funciona com um &i32 porque os operadores numéricos do Rust também aceitam referências. No filter, age é um &&i32, então **age segue as duas referências até chegar ao número. Comparações como b.cmp(a) funcionam direto com referências.
Correção 2: Escreva o tipo que o método realmente passa
Seção intitulada “Correção 2: Escreva o tipo que o método realmente passa”Escolha esta quando você quer deixar o tipo visível, por exemplo para facilitar a leitura de uma cadeia longa. Siga o help: do rustc e acrescente o &. Você também pode colocar o & no padrão: |&p| desmonta a referência e entrega uma cópia simples do número (isso funciona para tipos Copy, como inteiros):
fn main() { let prices = vec![120, 45, 300]; let ages = vec![12, 34, 17, 50]; let mut scores = vec![42, 7, 99, 15];
// escreva os tipos reais: & para .iter(), && dentro do filter let doubled: Vec<i32> = prices.iter().map(|p: &i32| p * 2).collect(); let adults: Vec<&i32> = ages.iter().filter(|age: &&i32| **age >= 18).collect(); scores.sort_by(|a: &i32, b: &i32| b.cmp(a));
// ou copie o número para fora com um padrão & let tax: Vec<i32> = prices.iter().map(|&p| p / 10).collect();
println!("{:?}", doubled); println!("{:?}", adults); println!("{:?}", scores); println!("{:?}", tax);}Saída:
[240, 90, 600][34, 50][99, 42, 15, 7][12, 4, 30]Repare na diferença: |p: &i32| quer dizer “p é uma referência”, enquanto |&p| quer dizer “eu recebo uma referência, me dê o que está por trás dela”.
Correção 3: Mude o que o iterador entrega
Seção intitulada “Correção 3: Mude o que o iterador entrega”Escolha esta quando você quer mesmo os valores, não referências. Há dois jeitos de obtê-los:
.iter().copied()transforma cada&i32em umi32. Use com tiposCopypequenos, como números,charebool. (.cloned()faz o mesmo para tipos comoString, clonando.).into_iter()numVecentrega os próprios itens. Depois disso oVecfoi consumido, então escolha esta quando não precisar mais dele.
fn main() { let prices = vec![120, 45, 300];
// .copied() transforma itens &i32 em itens i32 let doubled: Vec<i32> = prices.iter().copied().map(|p: i32| p * 2).collect(); println!("{:?}", doubled);
// .into_iter() num Vec entrega os próprios itens (e consome o Vec) let names = vec![String::from("ferris"), String::from("corro")]; let loud: Vec<String> = names.into_iter().map(|name: String| name.to_uppercase()).collect(); println!("{:?}", loud);}Saída:
[240, 90, 600]["FERRIS", "CORRO"]Se você ainda precisa de names depois do laço, continue com .iter() e receba &String. Tentar mover itens para fora de um Vec que você só pegou emprestado é outro erro, o E0507.
Correção 4: Faça a função com nome receber o que o iterador entrega
Seção intitulada “Correção 4: Faça a função com nome receber o que o iterador entrega”Escolha esta quando você passa uma função pelo nome, como .map(shout). Aí a mensagem diz “function” em vez de “closure”:
error[E0631]: type mismatch in function arguments --> src/main.rs:7:51 |1 | fn shout(word: &String) -> String { | --------------------------------- found signature defined here...7 | let loud: Vec<String> = words.into_iter().map(shout).collect(); | --- ^^^^^ expected due to this | | | required by a bound introduced by this call | = note: expected function signature `fn(&str) -> _` found function signature `fn(&String) -> _`Os itens são &str, mas shout quer &String. Um bom hábito: faça as funções que só leem texto receberem &str. Assim elas aceitam literais de string, &String e mais. Quando o iterador acrescenta um & extra (como faz o filter), envolva a chamada numa closure curta. Numa chamada de função normal, o Rust tira os & extras para você (isso se chama deref coercion), mas ele não consegue fazer isso quando você passa a função pelo nome:
// recebe &str: funciona com literais de string, &String e maisfn shout(word: &str) -> String { word.to_uppercase()}
fn is_long(word: &str) -> bool { word.len() > 4}
fn main() { let words = vec!["crab", "rustacean", "ferris"];
// os itens são &str, então shout pode ser passada pelo nome let loud: Vec<String> = words.iter().copied().map(shout).collect(); println!("{:?}", loud);
// filter entrega &&str: uma closure pequena deixa o Rust converter para você let long: Vec<&str> = words.iter().copied().filter(|w| is_long(w)).collect(); println!("{:?}", long);}Saída:
["CRAB", "RUSTACEAN", "FERRIS"]["rustacean", "ferris"]As palavras ficaram em inglês porque is_long conta bytes com .len(), e letras acentuadas ocupam 2 bytes. Mais sobre &str e String em Strings.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Ordenar com parâmetros tipados. scores.sort_by(|a: i32, b: i32| b.cmp(&a)) dá o E0631 com expected closure signature `for<'a, 'b> fn(&'a {integer}, &'b {integer}) -> _`. A parte for<'a, 'b> só quer dizer “para qualquer empréstimo”. Correção: |a, b| b.cmp(a). Veja Ordenação.
Entradas de map com tipos próprios. stock.iter().map(|(name, count): (String, i32)| ...) num HashMap<String, i32> dá expected closure signature `fn((&String, &_)) -> _`. O .iter() de um map empresta tanto a chave quanto o valor. Correção: tire a anotação, ou escreva (&String, &i32).
A sua própria função genérica. Se fn apply<F: Fn(i32) -> i32>(f: F, x: i32) recebe uma closure |x: f64| x / 2.0, o rustc mostra expected closure signature `fn(i32) -> _` e found closure signature `fn(f64) -> _`, apontando para o bound. Correção: faça a closure receber i32 (e converta dentro, x as f64 / 2.0), ou mude o bound se f64 era o que você queria. Veja Traits de closures.
for_each com String. words.iter().for_each(|w: String| ...) num Vec<&str> espera fn(&&str). Um &str não é uma String. Correção: |w|, ou |w: &&str|.
Erros relacionados
Seção intitulada “Erros relacionados”- E0593: closure takes the wrong number of arguments: o erro irmão, para a quantidade de parâmetros (por exemplo,
|a, b|quando é passada uma tupla). - E0057: closure called with the wrong number of arguments: você mesmo chama uma closure e passa valores de menos ou de mais.
- E0308: mismatched types: o erro geral de “tipo errado”, por exemplo quando o valor de retorno da closure tem o tipo errado, ou com um parâmetro do tipo ponteiro
fn(...). - E0599: no method found: o erro “trait bounds were not satisfied” de
collectousumque vem depois do E0631. Ele some junto com o E0631. - E0507: cannot move out of a borrowed value: quando você tenta tomar o ownership (posse) de itens que só pegou emprestado com
.iter(). - 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. let v = vec![1, 2, 3]; Que tipo a closure de v.iter().map(|x| ...) recebe?
2. A nota diz expected closure signature fn(&{integer}) -> _, found closure signature fn(i32) -> _. Qual é a correção mais rápida?
3. O que |&p| p * 2 faz dentro de prices.iter().map(...)?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0631 — Rust error codes index (em inglês): a explicação oficial deste erro.
- Processing a Series of Items with Iterators — The Rust Book (em inglês):
iter,into_itereiter_mut, e o que cada um entrega. - Closures — The Rust Book (em inglês): como os tipos dos parâmetros das closures são inferidos e quando vale a pena escrevê-los.
Iterator::copied— documentação da biblioteca padrão (em inglês): transforma um iterador de&Tnum iterador deT.slice::sort_by— documentação da biblioteca padrão (em inglês): a assinatura que mostra que a closure de comparação recebe duas referências.