Salta ai contenuti

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

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"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.emial); // ❌ typo: the field is `email`
}

(Il commento dice: errore di battitura, il campo si chiama email.)

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 abbia user.

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.

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.

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"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.email); // ✅ il vero nome del campo
}

Output:

▶ Provalo tu

Se 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: Ferris
Nessun utente con id 2
Primo utente: Ada
▶ Provalo tu

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

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 = 4
distanza al quadrato = 25
1920x1080
▶ Provalo tu

Le 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!
▶ Provalo tu

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.

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.

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