Errore Rust E0609: no field X on type T (campo inesistente)
E0609 significa: hai scritto value.something, ma il tipo di value non ha nessun campo chiamato something. Il più delle volte è un errore di battitura, oppure il valore non è proprio del tipo che pensi (un Option<User> invece di un User, per esempio). Il messaggio di errore ti dice il tipo esatto che vede Rust, e di solito basta questo per risolvere. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0609]: no field `emial` on type `User` --> src/main.rs:12:25 |12 | println!("{}", user.emial); // ❌ typo: the field is `email` | ^^^^^ unknown field |help: a field with a similar name exists |12 - println!("{}", user.emial); // ❌ typo: the field is `email`12 + println!("{}", user.email); // ❌ typo: the field is `email` |Il compilatore ti dice: «nessun campo emial sul tipo User». Poi aggiunge che esiste un campo con un nome simile e ti mostra la correzione: user.email.
Ecco il programma che lo provoca:
struct User { name: String, email: String,}
fn main() { let user = User { name: String::from("Ferris"), }; println!("{}", user.name); println!("{}", user.emial); // ❌ typo: the field is `email`}(Il commento dice: errore di battitura, il campo si chiama email.)
Cosa significa
Sezione intitolata “Cosa significa”La prima riga del messaggio ha due parti, e contano tutte e due:
no field `emial`: il nome dopo il punto che Rust non ha trovato.on type `User`: il tipo che Rust pensa abbiauser.
Quindi controlla prima il nome, poi il tipo. Se il tipo nel messaggio non è la struct che ti aspettavi, per esempio Option<User>, &Option<User>, Vec<User> o &T, il nome va bene: il problema è che prima devi tirare fuori la struct.
È come chiedere in un negozio «quello blu sul secondo scaffale» mentre sei nel negozio sbagliato. La richiesta è giusta, sei solo nel posto sbagliato.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”In Rust i campi di una struct sono fissati nel momento in cui la scrivi. Il compilatore conosce l’elenco completo e controlla ogni .campo in fase di compilazione. Non c’è il «lo cerco a runtime e ottengo undefined» di JavaScript, né l’AttributeError di Python: un nome di campo sbagliato non arriva mai nel tuo programma. Vedi Struct.
I tipi «contenitore» come Option<T> e Result<T, E> sono tipi a sé: la tua struct sta dentro di loro, ma loro non hanno i suoi campi. Rust non ci entra al posto tuo, perché il valore potrebbe proprio non esserci. Devi dire tu cosa succede quando manca. Vedi Option.
Il codice generico segue la stessa regola rigida. In fn greet<T>(x: &T), T può essere qualsiasi tipo, compresi i32 o bool, che non hanno campi. Rust controlla una funzione generica una sola volta per tutti i T possibili, quindi ti lascia usare solo ciò che promettono i tuoi vincoli di trait (trait bound). E i trait possono promettere solo metodi, non campi. Vedi Generici e Trait.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: correggi il nome
Sezione intitolata “Soluzione 1: correggi il nome”Scegli questa strada quando il tipo nel messaggio è la struct che ti aspettavi. Cerca la riga help: a field with a similar name exists (esiste un campo con un nome simile): di solito rustc sa esattamente quale campo intendevi:
struct User { name: String, email: String,}
fn main() { let user = User { name: String::from("Ferris"), }; println!("{}", user.name); println!("{}", user.email); // ✅ il vero nome del campo}Output:
FerrisSe non c’è nessun suggerimento, salta alla definizione della struct (nella maggior parte degli editor: Ctrl+clic o F12 sul tipo) e copia il nome da lì. I nomi dei campi distinguono maiuscole e minuscole: userName e user_name sono campi diversi.
Soluzione 2: tira fuori prima la struct dall’Option
Sezione intitolata “Soluzione 2: tira fuori prima la struct dall’Option”Scegli questa strada quando il messaggio dice che il tipo è Option<User>, Option<&User> o &Option<User>. Succede di continuo con le funzioni che potrebbero non trovare qualcosa, e con metodi come .first(), .last(), .get() e .find():
error[E0609]: no field `name` on type `Option<User>` --> src/main.rs:11:25 |11 | println!("{}", user.name); | ^^^^ unknown field |help: one of the expressions' fields has a field of the same name |11 | println!("{}", user.unwrap().name); | +++++++++Il suggerimento .unwrap() compila, ma fa andare in crash il programma quando il valore è None. Meglio decidere cosa deve succedere in entrambi i casi, con if let, match, oppure map più un valore predefinito:
struct User { name: String,}
fn find_user(id: u32) -> Option<User> { if id == 1 { Some(User { name: String::from("Ferris") }) } else { None }}
fn main() { // if let: esegue il codice solo quando c'è un User if let Some(user) = find_user(1) { println!("Trovato: {}", user.name); }
// match: gestisce entrambi i casi match find_user(2) { Some(user) => println!("Trovato: {}", user.name), None => println!("Nessun utente con id 2"), }
// map + unwrap_or: prende un campo, oppure usa un valore predefinito let users = vec![User { name: String::from("Ada") }]; let first_name = users.first().map(|u| u.name.as_str()).unwrap_or("nessuno"); println!("Primo utente: {first_name}");}Output:
Trovato: FerrisNessun utente con id 2Primo utente: AdaGli stessi pattern funzionano su un riferimento a un Option. Se maybe_user è un &Option<User>, scrivi if let Some(user) = maybe_user e user sarà un &User. (Per &Option<User> rustc non dà nessun suggerimento, quindi vale la pena ricordarselo.) Dentro una funzione che restituisce Option funziona anche let user = find_user(1)?;. Vedi Option e if let.
Soluzione 3: usa .0 e .1 sulle tuple struct
Sezione intitolata “Soluzione 3: usa .0 e .1 sulle tuple struct”Scegli questa strada quando il tipo è una tuple struct, come struct Point(i32, i32);. I suoi campi non hanno nomi, solo posizioni, quindi p.x dà no field `x` on type `Point` (con il suggerimento p.0). Usa la posizione, oppure scomponila in variabili con un nome. Se vuoi i nomi, trasformala in una struct con campi nominati:
struct Point(i32, i32); // tuple struct: i campi sono .0 e .1
struct Size { width: u32, // campi con nome: usa .width e .height height: u32,}
fn main() { let p = Point(3, 4); println!("x = {}, y = {}", p.0, p.1);
let Point(x, y) = p; // oppure scomponila in variabili con un nome println!("distanza al quadrato = {}", x * x + y * y);
let screen = Size { width: 1920, height: 1080 }; println!("{}x{}", screen.width, screen.height);}Output:
x = 3, y = 4distanza al quadrato = 251920x1080Le tuple normali funzionano allo stesso modo. pair.2 su una tupla di due elementi dà E0609 con la nota available fields are: `0`, `1` (i campi disponibili sono 0 e 1). Vedi Tuple.
Soluzione 4: su un T generico usa un metodo di trait invece di un campo
Sezione intitolata “Soluzione 4: su un T generico usa un metodo di trait invece di un campo”Scegli questa strada quando il messaggio dice on type `&T` (o T), con l’etichetta type parameter 'T' declared here. Un codice come questo non compila, anche se tutti i tipi che passi hanno un campo name:
fn greet<T>(animal: &T) { println!("Hello, {}!", animal.name); // ❌ no field `name` on type `&T`}I trait non possono richiedere campi, ma possono richiedere un metodo getter. Definisci un piccolo trait, implementalo per ogni tipo e aggiungilo come vincolo su T:
trait Named { fn name(&self) -> &str;}
struct Dog { name: String,}
struct Robot { model: String,}
impl Named for Dog { fn name(&self) -> &str { &self.name }}
impl Named for Robot { fn name(&self) -> &str { &self.model // il campo non deve nemmeno avere lo stesso nome }}
fn greet<T: Named>(thing: &T) { println!("Ciao, {}!", thing.name()); // una chiamata di metodo, non un campo}
fn main() { greet(&Dog { name: String::from("Rex") }); greet(&Robot { model: String::from("R2-D2") });}Output:
Ciao, Rex!Ciao, R2-D2!Se la funzione lavora sempre con un solo tipo, non ti serve affatto un generico: scrivi fn greet(animal: &Dog) e animal.name funziona. Vedi Trait.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Un metodo senza (). Scrivere word.len o cart.total (dove total è un metodo) è molto vicino a E0609, ma rustc dà un codice diverso: E0615, «attempted to take value of method», con il suggerimento «use parentheses to call the method» (usa le parentesi per chiamare il metodo). Su un Vec, scores.len dà E0616, «field len of struct Vec is private», perché Vec ha davvero un campo nascosto con quel nome. La soluzione è la stessa per entrambi: word.len(), scores.len(), cart.total(). E "hello".length è un normale E0609 su &str: in Rust si chiama len(). Vedi Stringhe.
Un Vec invece di un singolo elemento. dogs.name su un Vec<Dog> dà no field `name` on type `Vec<Dog>`. Una lista non ha un name, ce l’ha ogni elemento. Soluzione: prendine uno con dogs[0].name, oppure scorrila con for dog in &dogs { println!("{}", dog.name); }. Vedi Vettori.
Un Result invece del valore. load_config().port dà no field `port` on type `Result<Config, String>`, con lo stesso suggerimento .unwrap(). Soluzione: let config = load_config()?; dentro una funzione che restituisce Result, oppure un match. Vedi Result.
Un campo su un numero. age.value su un u32 ha ancora un altro codice: E0610, «u32 is a primitive type and therefore doesn’t have fields» (u32 è un tipo primitivo e quindi non ha campi). Numeri, bool e char non hanno nessun campo; probabilmente intendevi una variabile di tipo struct.
Errori correlati
Sezione intitolata “Errori correlati”- E0599: no method named x found: la versione di questo errore per i metodi. Spesso le cause sono le stesse: un errore di battitura, o un
Optionda scartare. - E0425: cannot find value in this scope: la variabile prima del punto non esiste proprio, oppure hai dimenticato
self.davanti a un campo. - E0308: mismatched types: quello che vedrai spesso subito dopo, se scarti un
Optionin un punto e non in un altro. E0615: attempted to take value of method: hai scrittox.leninvece dix.len().E0616: field is private: il campo esiste ma non èpub, comelensu unVec. Usa i metodi del tipo.E0560: struct has no field named x: lo stesso errore di battitura, ma mentre crei la struct (User { emial: ... }).E0610: primitive type doesn't have fields:.qualcosasu un numero, unboolo unchar.- Tutti gli errori del compilatore spiegati
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. L'errore dice che non c'è nessun campo name sul tipo Option<User>. Cosa ti sta dicendo?
2. Come leggi il primo campo di struct Meters(f64); da un valore m?
3. In fn show<T>(x: &T), come puoi leggere un nome da ogni T?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0609 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- Defining and Instantiating Structs — The Rust Book (in inglese): campi con nome, accesso con il punto e la sintassi di aggiornamento delle struct.
- Tuple Structs — The Rust Book (in inglese): struct i cui campi si leggono per posizione, come
.0. - The Option Enum — The Rust Book (in inglese): perché
Option<T>eTsono tipi diversi, e perché devi scartarlo. - Traits: Defining Shared Behavior — The Rust Book (in inglese): metodi dei trait e vincoli di trait, lo strumento giusto per il codice generico.