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.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”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 dereferencedIl 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);}Cosa significa
Sezione intitolata “Cosa significa”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:
visits.iter()ti dà riferimenti agli elementi:&u32,&u32, ….copied()trasforma ogni&u32in una semplice copiau32. I riferimenti spariscono.- Quindi in
.map(|count| ...),countè unu32, e*countchiede 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.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”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).
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: togli l’*
Sezione intitolata “Soluzione 1: togli l’*”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]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 dereferencedIl 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: 2553 punteggi, media 85Una 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 dereferencedIl 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 costosiLo 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 dereferencedIl 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 mSe 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.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”* 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.
Errori correlati
Sezione intitolata “Errori correlati”- 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
Sezione intitolata “Verifica veloce”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
Per approfondire
Sezione intitolata “Per approfondire”- E0614 — Rust error codes index (in inglese): la spiegazione ufficiale, con un esempio minimo e la correzione.
- References and Borrowing — The Rust Book (in inglese): che cosa significano
&e*, e perché un riferimento e un valore sono tipi diversi. - Treating Smart Pointers Like Regular References — The Rust Book (in inglese): come
*segue i riferimenti, e come il traitDereflo fa funzionare con i tuoi tipi. Iterator::copied— documentazione della libreria standard (in inglese): il metodo che trasforma elementi&Tin elementiT, il motivo più comune per cui un*diventa di troppo.- Destructuring pointers — Rust by Example (in inglese): la differenza tra
&valin un pattern e*valin un’espressione.