Pular para o conteúdo

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.

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 dereferenced

Em 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 ^^^^^^ 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:

  1. visits.iter() te dá referências aos itens: &u32, &u32, …
  2. .copied() transforma cada &u32 numa cópia comum, um u32. As referências sumiram.
  3. Então, em .map(|count| ...), count é um u32, e *count pede 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.

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.

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]
▶ Experimente você mesmo

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 dereferenced

O 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: 255
3 notas, média 85
▶ Experimente você mesmo

Uma 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 dereferenced

O 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 caros
▶ Experimente você mesmo

O 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 dereferenced

O 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 m
▶ Experimente você mesmo

Se 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.

* 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.

  • 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

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