Errore Rust E0610: primitive type doesn't have fields (campo su un numero)
E0610 significa: hai scritto something.field, ma something è un semplice numero (o un bool o un char), e questi non hanno nessun campo. Quasi sempre vuol dire che la variabile non è quello che pensi. Forse hai già tirato fuori il numero da una struct, una funzione ha restituito un numero invece di una struct, oppure hai scritto un .0 di troppo. Il messaggio ti dice il tipo esatto che vede Rust, quindi sei a un passo dalla soluzione. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields --> src/main.rs:3:22 |3 | println!("{}", x.value); // ❌ x is a number, it has no `value` field | ^^^^^Il compilatore ti dice: «{integer} è un tipo primitivo e quindi non ha campi».
Ecco il programma che lo provoca:
fn main() { let x = 5; println!("{}", x.value); // ❌ x is a number, it has no `value` field}(Il commento dice: x è un numero, non ha un campo value.)
Cosa significa
Sezione intitolata “Cosa significa”Il tipo tra apici inversi è quello che Rust pensa sia la tua variabile. Qui è `{integer}`, che vuol dire «un numero intero, non ho ancora deciso di che tipo» (Rust sceglie i32 più tardi, se nient’altro decide). Vedrai anche `{float}` per i decimali, oppure un tipo concreto come u32, usize, f64, bool o char.
I ^^^^^ indicano il nome dopo il punto. Rust non sta dicendo «value è scritto male». Sta dicendo «un numero non può avere nessun campo, comunque lo chiami».
Quindi la domanda da farsi è: perché questa variabile è un numero? È come chiedere «il cartellino del prezzo del 5». Un prodotto ha un cartellino. Il numero 5 è solo un numero.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”I tipi primitivi come i32, u32, f64, bool e char sono valori singoli integrati nel linguaggio. Non sono struct, quindi dentro non c’è niente da raggiungere con un punto e un nome. Vedi Tipi di dati.
Però hanno dei metodi: x.abs(), x.pow(2), price.round(), c.is_alphabetic(). I metodi si chiamano con (). I campi sono dati salvati dentro una struct, e li hanno solo struct, tuple e union.
Siccome Rust controlla ogni .campo in fase di compilazione, questa confusione non può mai diventare un crash o un undefined silenzioso a runtime. Lo scopri subito, alla riga esatta.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: usa direttamente il numero
Sezione intitolata “Soluzione 1: usa direttamente il numero”Scegli questa strada quando hai già tirato fuori il campo dalla struct prima. Dopo let count = counter.value;, count è il numero, quindi count.value chiede un campo su un u32:
error[E0610]: `u32` is a primitive type and therefore doesn't have fields --> src/main.rs:8:26 |8 | println!("{}", count.value); | ^^^^^Usa il numero così com’è, oppure torna alla struct e leggi il campo da lì:
struct Counter { value: u32,}
fn main() { let mut counter = Counter { value: 0 }; counter.value += 3;
let count = counter.value; // ora count è un semplice u32 println!("{}", count); // ✅ usa il numero stesso println!("{}", counter.value); // ✅ oppure leggi il campo dalla struct}Output:
33Non sai che tipo ha una variabile? Passaci sopra con il mouse nel tuo editor (con rust-analyzer, vedi Strumenti Rust). Oppure scrivi per un momento let _: () = count;: rustc risponde con expected `()`, found `usize` e ti dice il tipo vero. Poi cancella la riga.
Soluzione 2: restituisci o salva la struct, non il numero
Sezione intitolata “Soluzione 2: restituisci o salva la struct, non il numero”Scegli questa strada quando il valore arriva da una funzione o da una collezione, e ti aspettavi una struct. Due casi comuni:
- Una funzione come
fn find_order() -> u32restituisce l’id di un ordine, e poiorder.totalfallisce perchéorderè unu32. - Una
HashMap<&str, u32>salva solo i punteggi, e poiscores["ana"].scorefallisce perché ogni valore è unu32, non unPlayer.
Decidi cosa vuoi davvero. Se ti servono più dati, cambia il tipo restituito o il tipo dei valori nella struct:
use std::collections::HashMap;
struct Order { id: u32, total: u32, // in centesimi}
// restituisce l'intero Order, non solo un numerofn new_order(id: u32, total: u32) -> Order { Order { id, total }}
struct Player { score: u32, level: u8,}
fn main() { let order = new_order(7, 1999); println!("L'ordine {} costa {} centesimi", order.id, order.total);
// la mappa salva interi Player, non solo i loro punteggi let mut players: HashMap<&str, Player> = HashMap::new(); players.insert("ana", Player { score: 10, level: 2 });
let ana = &players["ana"]; println!("ana: punteggio {}, livello {}", ana.score, ana.level);}Output:
L'ordine 7 costa 1999 centesimiana: punteggio 10, livello 2Se ti basta un numero, tieni il u32 e togli semplicemente la parte .campo. Vedi Struct e HashMap.
Soluzione 3: togli il .0 di troppo
Sezione intitolata “Soluzione 3: togli il .0 di troppo”Scegli questa strada quando l’errore indica un .0 o un .1. Tuple e tuple struct usano posizioni per i loro campi, ed è facile scendere di un livello di troppo. Dopo let x = point.0;, x è già il numero, quindi x.0 dà `{integer}` is a primitive type. Lo stesso succede con un tipo «contenitore» come struct Meters(u32);: un .0 tira fuori l’u32, un secondo .0 fallisce.
struct Meters(u32);
fn main() { let point = (3, 4); let x = point.0; // x è già il numero 3 println!("x = {}", x); // non x.0
let m = Meters(42); println!("{} m", m.0); // un .0 tira fuori l'u32 dalla tuple struct
let line = ((0, 0), (3, 4)); // tuple annidate: una posizione per livello println!("y finale = {}", line.1.1);}Output:
x = 342 my finale = 4Conta i livelli: ogni .0 o .1 scende di un livello dentro una tupla. Quando arrivi a un numero, fermati. Se le posizioni diventano confuse, dai loro un nome con una struct. Vedi Tuple.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Un metodo senza (). price.round su un f64, o x.abs su un i32, non è E0610 ma E0615, «attempted to take value of method», perché quei nomi sono metodi. Soluzione: price.round(), x.abs(). Una sorpresa: se il tipo è ancora {integer} (come in let x = -5;), anche x.abs dà E0610, e poi x.abs() dà E0689 «can’t call method abs on ambiguous numeric type {integer}». Scegli un tipo, let x: i32 = -5;, e x.abs() funziona.
Un riferimento a un numero. Se la variabile è un &u32 invece di un u32, il codice diventa E0609: no field `value` on type `&u32`. Un caso tipico è un ciclo come for (name, score) in &players, dove score è un &i32, e score.1 dà no field `1` on type `&{integer}`. Stessa causa, stesse soluzioni: score è già il numero.
Il risultato di len() o count(). let len = scores.len(); ti dà un usize, quindi len.count fallisce con `usize` is a primitive type. Soluzione: usa direttamente len.
Numeri con un’unità di misura. Se vuoi price.cents o distance.meters, crea una piccola struct: struct Price { cents: u64 }. Allora price.cents funziona, e il tipo ti impedisce di confondere i prezzi con altri numeri. Vedi Struct.
Errori correlati
Sezione intitolata “Errori correlati”- E0609: no field
xon typeT: il tipo non ha un campo con quel nome. Con i riferimenti come&u32ottieni questo (non E0610). - E0615: attempted to take value of method: hai scritto
price.roundinvece diprice.round(). E0689: can't call method on ambiguous numeric type: un metodo su un numero il cui tipo non è ancora scelto. Aggiungi un tipo comei32.- E0599: no method named x found: la versione per i metodi, per esempio un metodo che i numeri non hanno.
- E0308: mismatched types: compare spesso vicino a E0610 quando una funzione restituisce un numero ma ti aspettavi una struct.
- Tutti gli errori del compilatore spiegati
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Dopo let total = cart.total; (dove total è un campo u32), cosa dà total.amount?
2. Cosa vuol dire {integer} nel messaggio?
3. let x = point.0; e poi x.0 dà E0610. Qual è la soluzione?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0610 — Rust error codes index (in inglese): la spiegazione ufficiale di questo errore.
- Data Types — The Rust Book (in inglese): i tipi scalari (interi, float,
bool,char) e i tipi composti (tuple, array). - Tuple Structs — The Rust Book (in inglese): tipi contenitore come
Meters(u32)e come funziona.0su di loro. - Primitives — Rust by Example (in inglese): i tipi integrati, con esempi eseguibili.
i32— libreria standard di Rust (in inglese): tutti i metodi di un intero (e vedrai che si chiamano tutti con()).