Salta ai contenuti

Errore Rust E0614: type cannot be dereferenced (asterisco di troppo)

E0614 significa: hai scritto * davanti a qualcosa che non è un riferimento, quindi non c’è niente da seguire. L’operatore * significa «vai al valore a cui punta questo riferimento». Un semplice u32, i32 o una struct non puntano da nessuna parte: sono il valore. Di solito succede in closure e cicli, dove è facile perdere il conto se hai un &u32 o un u32. rustc ti dice il tipo vero nella prima riga, e la correzione è quasi sempre cancellare un *. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)

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

Il compilatore dice: il tipo u32 non si può dereferenziare. Questo è il programma che produce l’errore:

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);
}

Il ^^^^^^ indica *count alla riga 3, con l’etichetta «can’t be dereferenced» (non si può dereferenziare). La prima riga ti dice che cosa è davvero count: un u32, non un &u32.

Segui i tipi lungo la catena:

  1. visits.iter() ti dà riferimenti agli elementi: &u32, &u32, …
  2. .copied() trasforma ogni &u32 in una semplice copia u32. I riferimenti spariscono.
  3. Quindi in .map(|count| ...), count è un u32, e *count chiede a Rust di seguire un puntatore che non esiste.

Quando la prima riga dice type `{integer}` cannot be dereferenced, è la stessa cosa. {integer} è il modo in cui rustc scrive «un tipo intero che non ho ancora deciso», di solito un numero come 72 a cui non hai mai dato un tipo.

È come chiedere a qualcuno «che cosa c’è a questo indirizzo?» porgendogli il pacco invece di un foglietto con un indirizzo. Ha già la cosa in mano; non c’è nessun indirizzo dove andare.

In Rust un riferimento (&T) e il valore stesso (T) sono tipi diversi. * funziona solo con cose che puntano da qualche parte: i riferimenti &T e &mut T, gli smart pointer come Box<T> e Rc<T>, e qualsiasi tipo che implementa il trait Deref. Un u32 o una struct semplice non hanno niente di tutto questo, quindi * su di loro non ha significato.

Rust potrebbe ignorare in silenzio l’* di troppo, ma non lo fa. Un * in più di solito significa che ti sbagli sul tipo che hai, e quella confusione tende a causare altri bug: confrontare le cose sbagliate, o spostare un valore che volevi prendere in prestito. Quindi rustc si ferma e ti dice il tipo vero. Vedi Prestito (borrowing) in Rust e Smart pointer in Rust.

Lo sbaglio opposto ha il suo errore. Se dimentichi un * su un riferimento, come count > 10 dove count è un &u32, ottieni E0308 (tipi che non corrispondono).

Scegli questa quasi sempre, soprattutto dopo .copied(), .cloned(), .into_iter() su un Vec, o qualsiasi altra cosa che ti consegna valori. Il valore è già lì, quindi usalo e basta:

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);
// `filter` presta ogni elemento alla closure, quindi qui `count` È un `&u32`
// e l'`*` è giusto.
let busy: Vec<u32> = visits.iter().copied().filter(|count| *count > 10).collect();
println!("{:?}", busy);
}

Output:

[24, 0, 14, 60]
[12, 30]
▶ Provalo tu

Quella seconda parte confonde molte persone: nella stessa catena, map riceve l’elemento stesso e filter riceve un riferimento all’elemento. Nel dubbio, guarda il metodo nella documentazione. filter prende FnMut(&Self::Item) -> bool, con una &. Vedi Iteratori in Rust e Closure in Rust.

Soluzione 2: in un ciclo for, scegli tra valori e riferimenti

Sezione intitolata “Soluzione 2: in un ciclo for, scegli tra valori e riferimenti”

Scegli questa quando l’* sta dentro un ciclo for. for score in scores smonta il Vec e ti dà ogni valore, quindi *score fallisce:

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

Il programma che non compila:

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 togli l’*, oppure fai il ciclo su &scores. Allora ogni score è un &i32, l’* è corretto, e puoi ancora usare il Vec dopo il ciclo:

fn main() {
let scores = vec![72, 95, 88];
let mut total = 0;
for score in &scores {
total += *score; // ora `score` è un `&i32`
}
println!("totale: {}", total);
// `scores` non è stato spostato nel ciclo, quindi è ancora qui.
println!("{} punteggi, media {}", scores.len(), total / scores.len() as i32);
}

Output:

totale: 255
3 punteggi, media 85
▶ Provalo tu

Una regola veloce: for x in v dà valori, for x in &v dà riferimenti &, for x in &mut v dà riferimenti &mut. Solo gli ultimi due hanno bisogno di un *. Vedi Cicli for in Rust e Vettori in Rust.

Soluzione 3: confronta il riferimento con &x, oppure usa *x, ma non entrambi

Sezione intitolata “Soluzione 3: confronta il riferimento con &x, oppure usa *x, ma non entrambi”

Scegli questa quando la tua closure o il tuo ciclo ha già una & nel pattern, come |&p|. Una & in un pattern spacchetta il riferimento per te, quindi p è un semplice numero e *p è un passo di troppo:

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

Il programma che non compila:

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);
}

Scegli un solo modo per liberarti del riferimento. Le due righe qui sotto fanno la stessa cosa:

fn main() {
let prices = [3, 5, 8];
let with_fee: Vec<i32> = prices.iter().map(|&p| p + 1).collect(); // il pattern toglie la `&`
let same: Vec<i32> = prices.iter().map(|p| *p + 1).collect(); // l'`*` toglie la `&`
println!("{:?} {:?}", with_fee, same);
// `filter` su `.iter()` riceve `&&i32`: due `&`, quindi il pattern è `&&p`.
let pricey = prices.iter().filter(|&&p| p > 4).count();
println!("{} articoli costosi", pricey);
}

Output:

[4, 6, 9] [4, 6, 9]
2 articoli costosi
▶ Provalo tu

Lo stesso vale per i cicli: for &(name, qty) in &orders spacchetta ogni riferimento, quindi usa name e qty senza *. Vedi Pattern matching in Rust.

Soluzione 4: controlla il tipo, e per i wrapper usa il campo o Deref

Sezione intitolata “Soluzione 4: controlla il tipo, e per i wrapper usa il campo o Deref”

Scegli questa quando la prima riga nomina un tuo tipo, come type `Meters` cannot be dereferenced. Anche una struct non è un riferimento, pure quando racchiude un solo valore:

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

Il programma che non compila:

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);
}

Per prendere il numero dentro una tuple struct, usa il suo campo, .0:

struct Meters(f64);
fn main() {
let run = Meters(2.5);
let longer = run.0 + 1.0;
println!("{} m", longer);
}

Output:

3.5 m
▶ Provalo tu

Se il tuo tipo è davvero un wrapper simile a uno smart pointer e vuoi che * funzioni, implementa std::ops::Deref per lui. Fallo solo per i tipi wrapper; per un «newtype» come Meters, .0 o un metodo sono più chiari. Non sai che tipo ha una variabile? Passaci sopra il mouse nel tuo editor (rust-analyzer mostra il tipo), oppure fidati della prima riga: nomina sempre il tipo vero. Vedi Struct in Rust e Smart pointer in Rust.

* sul campo di una struct. *config.retries, dove config è una struct (o un riferimento a una struct) e retries è un u32, dà type `u32` cannot be dereferenced. Il . segue già il riferimento per te. Correzione: config.retries.

Un ciclo su una HashMap per valore. for (word, count) in counts { total += *count; } dà type `u32` cannot be dereferenced, perché il ciclo su counts stessa dà chiavi e valori posseduti. Correzione: togli l’*, oppure fai il ciclo su &counts se dopo ti serve ancora la mappa. Vedi HashMap in Rust.

Dopo .copied().max() o .cloned().find(...). Questi danno Option<u32>, non Option<&u32>, quindi in if let Some(best) = ... { *best } l’* è di troppo. Correzione: usa direttamente best. Senza .copied(), .iter().max() dà Option<&u32> e *best è giusto.

  • E0308: tipi che non corrispondono; lo sbaglio opposto, un * mancante dove un riferimento incontra un valore semplice.
  • E0369: un’operazione binaria non si può applicare a un tipo, un altro errore che può capitare quando riferimenti e valori si mescolano.
  • E0507: non puoi spostare fuori da un riferimento; qui * funziona, ma sposterebbe un valore che non è Copy.
  • E0610: {integer} è un tipo primitivo e non ha campi, il cugino «è solo un numero» di questo errore.
  • E0599: metodo non trovato, quando il valore non è del tipo che pensavi.
  • Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.

Verifica veloce

1. In visits.iter().copied().map(|count| *count + 1), perché *count dà E0614?

2. for score in &scores { total += *score; } dove scores è un Vec<i32>. Compila?

3. prices.iter().map(|&p| *p * 2) dà E0614. Qual è una versione corretta?

Punteggio: 0 / 3