Erro E0614 do Rust: type cannot be dereferenced (não é uma referência)
O E0614 quer dizer: você escreveu * na frente de algo que não é uma referência, então não há nada para seguir. O operador * quer dizer “vá até o valor para onde esta referência aponta”. Um u32, um i32 ou uma struct comum não apontam para lugar nenhum: eles são o valor. Isso costuma acontecer em closures e laços, onde é fácil perder a conta de se você tem um &u32 ou um u32. O rustc diz o tipo de verdade no título da mensagem, e a correção quase sempre é apagar um *. Esta página faz parte do guia de erros do compilador.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0614]: type `u32` cannot be dereferenced --> src/main.rs:3:64 |3 | let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count` is already a `u32` | ^^^^^^ can't be dereferencedEm português: “o tipo u32 não pode ser desreferenciado”. A seta marca a expressão que não pode ser desreferenciada.
Este é o programa que gera o erro:
fn main() { let visits: Vec<u32> = vec![12, 0, 7, 30]; let doubled: Vec<u32> = visits.iter().copied().map(|count| *count * 2).collect(); // ❌ `count` is already a `u32` println!("{:?}", doubled);}(O comentário quer dizer “count já é um u32”. Ele fica em inglês porque aparece na saída do compilador.)
O que significa
Seção intitulada “O que significa”O ^^^^^^ aponta para *count na linha 3, com a etiqueta “can’t be dereferenced” (não pode ser desreferenciado). O título diz o que count é de verdade: um u32, e não um &u32.
Siga os tipos ao longo da cadeia:
visits.iter()te dá referências aos itens:&u32,&u32, ….copied()transforma cada&u32numa cópia comum, umu32. As referências sumiram.- Então, em
.map(|count| ...),counté umu32, e*countpede ao Rust para seguir um ponteiro que não existe.
Quando o título diz type `{integer}` cannot be dereferenced, é a mesma coisa. {integer} é como o rustc escreve “algum tipo inteiro que ainda não decidi”, normalmente um número como 72 ao qual você nunca deu um tipo.
É como perguntar a alguém “o que tem neste endereço?” e entregar o pacote em vez de um papel com o endereço escrito. A pessoa já está com a coisa na mão; não há endereço para ir.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Em Rust, uma referência (&T) e o próprio valor (T) são tipos diferentes. O * só funciona com coisas que apontam para algum lugar: referências &T e &mut T, smart pointers como Box<T> e Rc<T>, e qualquer tipo que implemente a trait Deref. Um u32 ou uma struct comum não têm nada disso, então um * neles não tem sentido.
O Rust poderia ignorar o * a mais em silêncio, mas não faz isso. Um * sobrando normalmente quer dizer que você se enganou sobre o tipo que tem nas mãos, e essa confusão costuma causar outros bugs: comparar as coisas erradas, ou mover um valor que você queria pegar emprestado. Por isso o rustc para e te diz o tipo real. Veja Borrowing e Smart pointers.
O engano contrário tem o seu próprio erro. Se você esquece um * numa referência, como em count > 10 com count sendo um &u32, você recebe o E0308, tipos incompatíveis.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Tire o *
Seção intitulada “Correção 1: Tire o *”Escolha esta quase sempre, principalmente depois de .copied(), .cloned(), .into_iter() num Vec, ou qualquer outra coisa que te entregue valores. O valor já está ali, então é só usar:
fn main() { let visits: Vec<u32> = vec![12, 0, 7, 30]; let doubled: Vec<u32> = visits.iter().copied().map(|count| count * 2).collect(); println!("{:?}", doubled);
// O `filter` empresta cada item para a closure, então aqui `count` É um `&u32` // e o `*` está certo. let busy: Vec<u32> = visits.iter().copied().filter(|count| *count > 10).collect(); println!("{:?}", busy);}Saída:
[24, 0, 14, 60][12, 30]Essa segunda parte confunde muita gente: na mesma cadeia, o map recebe o próprio item e o filter recebe uma referência ao item. Na dúvida, olhe o método na documentação. O filter recebe FnMut(&Self::Item) -> bool, com um &. Veja Iteradores e Closures.
Correção 2: Num laço for, escolha entre valores e referências
Seção intitulada “Correção 2: Num laço for, escolha entre valores e referências”Escolha esta quando o * está dentro de um laço for. for score in scores desmonta o Vec e te entrega cada valor, então *score falha:
error[E0614]: type `{integer}` cannot be dereferenced --> src/main.rs:5:18 |5 | total += *score; // ❌ `score` is a number, not a reference | ^^^^^^ can't be dereferencedO programa com erro:
fn main() { let scores = vec![72, 95, 88]; let mut total = 0; for score in scores { total += *score; // ❌ `score` is a number, not a reference } println!("total: {}", total);}(O comentário em inglês diz “score é um número, não uma referência”.) Ou você tira o *, ou percorre &scores. Aí cada score é um &i32, o * está certo, e você ainda pode usar o Vec depois do laço:
fn main() { let scores = vec![72, 95, 88];
let mut total = 0; for score in &scores { total += *score; // agora `score` é um `&i32` } println!("total: {}", total);
// `scores` não foi movido para dentro do laço, então continua aqui. println!("{} notas, média {}", scores.len(), total / scores.len() as i32);}Saída:
total: 2553 notas, média 85Uma regra rápida: for x in v te dá valores, for x in &v te dá referências &, e for x in &mut v te dá referências &mut. Só os dois últimos precisam de *. Veja Laços for e Vetores.
Correção 3: Desmonte a referência com &x ou use *x, mas não os dois
Seção intitulada “Correção 3: Desmonte a referência com &x ou use *x, mas não os dois”Escolha esta quando a sua closure ou o seu laço já tem um & no padrão, como |&p|. Um & num padrão desmonta a referência para você, então p é um número comum e *p é um passo a mais:
error[E0614]: type `{integer}` cannot be dereferenced --> src/main.rs:3:53 |3 | let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p` already removed the `&` | ^^ can't be dereferencedO programa com erro:
fn main() { let prices = [3, 5, 8]; let with_fee: Vec<i32> = prices.iter().map(|&p| *p + 1).collect(); // ❌ `&p` already removed the `&` println!("{:?}", with_fee);}(O comentário em inglês diz “o &p já tirou o &”.) Escolha um jeito só de se livrar da referência. As duas linhas abaixo fazem a mesma coisa:
fn main() { let prices = [3, 5, 8];
let with_fee: Vec<i32> = prices.iter().map(|&p| p + 1).collect(); // o padrão tira o `&` let same: Vec<i32> = prices.iter().map(|p| *p + 1).collect(); // o `*` tira o `&` println!("{:?} {:?}", with_fee, same);
// O `filter` em `.iter()` recebe `&&i32`: dois `&`, então o padrão é `&&p`. let pricey = prices.iter().filter(|&&p| p > 4).count(); println!("{} itens caros", pricey);}Saída:
[4, 6, 9] [4, 6, 9]2 itens carosO mesmo vale para laços: for &(name, qty) in &orders desmonta cada referência, então use name e qty sem *. Veja Pattern matching.
Correção 4: Confira o tipo, e use o campo ou Deref em wrappers
Seção intitulada “Correção 4: Confira o tipo, e use o campo ou Deref em wrappers”Escolha esta quando o título da mensagem cita um tipo seu, como type `Meters` cannot be dereferenced. Uma struct também não é uma referência, mesmo quando guarda um valor só:
error[E0614]: type `Meters` cannot be dereferenced --> src/main.rs:5:18 |5 | let longer = *run + 1.0; // ❌ `run` is a `Meters`, not a reference | ^^^^ can't be dereferencedO programa com erro:
struct Meters(f64);
fn main() { let run = Meters(2.5); let longer = *run + 1.0; // ❌ `run` is a `Meters`, not a reference println!("{} m", longer);}(O comentário em inglês diz “run é um Meters, não uma referência”.) Para pegar o número de dentro de uma tuple struct, use o campo dela, .0:
struct Meters(f64);
fn main() { let run = Meters(2.5); let longer = run.0 + 1.0; println!("{} m", longer);}Saída:
3.5 mSe o seu tipo é mesmo um wrapper (embrulho) no estilo de um smart pointer e você quer que o * funcione, implemente std::ops::Deref para ele. Faça isso só para tipos wrapper; para um “newtype” como Meters, o .0 ou um método fica mais claro. Não sabe qual é o tipo de uma variável? Passe o mouse sobre ela no editor (o rust-analyzer mostra o tipo), ou confie no título da mensagem: ele sempre diz o tipo real. Veja Structs e Smart pointers.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”* num campo de struct. *config.retries, em que config é uma struct (ou uma referência a uma) e retries é um u32, dá type `u32` cannot be dereferenced. O . já segue a referência para você. Correção: config.retries.
Percorrer um HashMap por valor. for (word, count) in counts { total += *count; } dá type `u32` cannot be dereferenced, porque percorrer o próprio counts te dá chaves e valores com posse (owned). Correção: tire o *, ou percorra &counts se você precisar do mapa depois. Veja HashMap.
Depois de .copied().max() ou .cloned().find(...). Eles dão um Option<u32>, e não um Option<&u32>, então em if let Some(best) = ... { *best } o * está sobrando. Correção: use best direto. Sem o .copied(), .iter().max() dá Option<&u32> e aí *best está certo.
Erros relacionados
Seção intitulada “Erros relacionados”- E0308: tipos incompatíveis; o engano contrário, um
*que falta onde uma referência encontra um valor comum. - E0369: uma operação binária que não pode ser aplicada a um tipo, outro erro que aparece quando referências e valores se misturam.
- E0507: não é possível mover para fora de uma referência; aqui o
*funciona, mas moveria um valor que não éCopy. - E0610:
{integer}é um tipo primitivo e não tem campos, o primo “é só um número” deste erro. - E0599: nenhum método encontrado, quando o valor não é do tipo que você achava.
- Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. Em visits.iter().copied().map(|count| *count + 1), por que *count dá E0614?
2. for score in &scores { total += *score; }, com scores sendo um Vec<i32>. Isso compila?
3. prices.iter().map(|&p| *p * 2) dá E0614. Qual é uma versão correta?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0614 — Rust error codes index (em inglês): a explicação oficial, com um exemplo mínimo e a correção.
- References and Borrowing — The Rust Book (em inglês): o que
&e*querem dizer, e por que uma referência e um valor são tipos diferentes. - Treating Smart Pointers Like Regular References — The Rust Book (em inglês): como o
*segue referências, e como a traitDereffaz isso funcionar nos seus próprios tipos. Iterator::copied— documentação da biblioteca padrão (em inglês): o método que transforma itens&Tem itensT, o motivo mais comum para um*ficar sobrando.- Destructuring pointers — Rust by Example (em inglês): a diferença entre
&valnum padrão e*valnuma expressão.