Erreur Rust E0610 : primitive type doesn't have fields (champ sur un nombre)
E0610 veut dire : vous avez écrit something.field, mais something est un simple nombre (ou un bool, ou un char), et ces types n’ont aucun champ. Presque toujours, la variable n’est pas ce que vous croyez. Peut-être avez-vous déjà sorti le nombre d’une struct, une fonction a renvoyé un nombre au lieu d’une struct, ou vous avez écrit un .0 de trop. Le message vous indique le type exact que Rust voit : vous êtes donc à un pas de la solution. (Cette page fait partie de Les erreurs du compilateur Rust expliquées.)
Le message d’erreur
Section intitulée « Le message d’erreur »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 | ^^^^^Le compilateur vous dit : « {integer} est un type primitif et n’a donc pas de champs ».
Voici le programme qui la provoque :
fn main() { let x = 5; println!("{}", x.value); // ❌ x is a number, it has no `value` field}(Le commentaire dit : x est un nombre, il n’a pas de champ value.)
Ce que ça veut dire
Section intitulée « Ce que ça veut dire »Le type entre backticks est ce que Rust pense de votre variable. Ici, c’est `{integer}`, ce qui veut dire « un nombre entier, mais je n’ai pas encore décidé de quelle sorte » (Rust choisit i32 plus tard si rien d’autre ne décide). Vous verrez aussi `{float}` pour les nombres à virgule, ou un type précis comme u32, usize, f64, bool ou char.
Les ^^^^^ pointent le nom après le point. Rust ne vous dit pas « value est mal écrit ». Il vous dit « un nombre ne peut avoir aucun champ, quel que soit son nom ».
La question à se poser est donc : pourquoi cette variable est-elle un nombre ? C’est comme demander « l’étiquette de prix du 5 ». Un produit a une étiquette de prix. Le nombre 5, lui, n’est qu’un nombre.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »Les types primitifs comme i32, u32, f64, bool et char sont des valeurs simples intégrées au langage. Ce ne sont pas des structs : il n’y a rien à l’intérieur que l’on pourrait atteindre avec un point et un nom. Voir Les types de données.
Ils ont en revanche des méthodes : x.abs(), x.pow(2), price.round(), c.is_alphabetic(). Les méthodes s’appellent avec (). Les champs, eux, sont des données rangées dans une struct, et seuls les structs, les tuples et les unions en ont.
Comme Rust vérifie chaque .field à la compilation, cette confusion ne peut jamais devenir un plantage ou un undefined silencieux à l’exécution. Vous le savez tout de suite, à la ligne exacte.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : utiliser le nombre directement
Section intitulée « Solution 1 : utiliser le nombre directement »Choisissez-la quand vous avez déjà sorti le champ de la struct plus haut. Après let count = counter.value;, count est le nombre, donc count.value demande un champ sur un u32 :
error[E0610]: `u32` is a primitive type and therefore doesn't have fields --> src/main.rs:8:26 |8 | println!("{}", count.value); | ^^^^^Utilisez soit le nombre tel quel, soit revenez à la struct et lisez-y le champ :
struct Counter { value: u32,}
fn main() { let mut counter = Counter { value: 0 }; counter.value += 3;
let count = counter.value; // count est maintenant un simple u32 println!("{}", count); // ✅ on utilise le nombre lui-même println!("{}", counter.value); // ✅ ou on lit le champ de la struct}Sortie :
33Vous ne savez pas quel est le type d’une variable ? Survolez-la dans votre éditeur (avec rust-analyzer, voir Les outils Rust). Ou écrivez un instant let _: () = count; : rustc répond par expected `()`, found `usize` (attendu (), trouvé usize) et vous donne ainsi le vrai type. Supprimez la ligne ensuite.
Solution 2 : renvoyer ou stocker la struct, pas le nombre
Section intitulée « Solution 2 : renvoyer ou stocker la struct, pas le nombre »Choisissez-la quand la valeur vient d’une fonction ou d’une collection et que vous attendiez une struct. Deux cas fréquents :
- Une fonction comme
fn find_order() -> u32renvoie l’identifiant d’une commande, puisorder.totaléchoue parce queorderest unu32. - Une
HashMap<&str, u32>ne stocke que des scores, puisscores["ana"].scoreéchoue parce que chaque valeur est unu32, pas unPlayer.
Décidez de ce que vous voulez vraiment. Si vous avez besoin de plusieurs données, changez le type de retour ou le type des valeurs pour la struct :
use std::collections::HashMap;
struct Order { id: u32, total: u32, // en centimes}
// renvoie toute la commande, pas juste un nombrefn 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!("La commande {} coûte {} centimes", order.id, order.total);
// la map stocke des Player entiers, pas seulement leurs scores let mut players: HashMap<&str, Player> = HashMap::new(); players.insert("ana", Player { score: 10, level: 2 });
let ana = &players["ana"]; println!("ana : score {}, niveau {}", ana.score, ana.level);}Sortie :
La commande 7 coûte 1999 centimesana : score 10, niveau 2Si un nombre vous suffit, gardez le u32 et retirez simplement la partie .field. Voir Les structs et Les HashMap.
Solution 3 : retirer le .0 en trop
Section intitulée « Solution 3 : retirer le .0 en trop »Choisissez-la quand l’erreur pointe un .0 ou un .1. Les tuples et les tuple structs utilisent des positions pour leurs champs, et il est facile de descendre d’un niveau de trop. Après let x = point.0;, x est déjà le nombre, donc x.0 donne `{integer}` is a primitive type. C’est pareil avec un type enveloppe comme struct Meters(u32); : un .0 sort le u32, et un deuxième .0 échoue.
struct Meters(u32);
fn main() { let point = (3, 4); let x = point.0; // x est déjà le nombre 3 println!("x = {}", x); // pas x.0
let m = Meters(42); println!("{} m", m.0); // un seul .0 sort le u32 de la tuple struct
let line = ((0, 0), (3, 4)); // tuples imbriqués : une position par niveau println!("y de fin = {}", line.1.1);}Sortie :
x = 342 my de fin = 4Comptez les niveaux : chaque .0 ou .1 descend d’un niveau dans un tuple. Quand vous arrivez à un nombre, arrêtez-vous. Si les positions deviennent confuses, donnez-leur plutôt des noms avec une struct. Voir Accéder aux éléments par position et Tuple ou struct ?
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Une méthode sans (). price.round sur un f64, ou x.abs sur un i32, ne donne pas E0610 mais E0615, « attempted to take value of method » (tentative de prendre la valeur d’une méthode), parce que ces noms sont des méthodes. Correction : price.round(), x.abs(). Une surprise : si le type est encore {integer} (comme dans let x = -5;), même x.abs donne E0610, et x.abs() donne ensuite E0689 « can’t call method abs on ambiguous numeric type {integer} » (impossible d’appeler la méthode abs sur un type numérique ambigu). Choisissez un type, let x: i32 = -5;, et x.abs() fonctionne.
Une référence vers un nombre. Si la variable est un &u32 au lieu d’un u32, le code devient E0609 : no field `value` on type `&u32`. On le rencontre souvent dans une boucle comme for (name, score) in &players, où score est un &i32, et où score.1 donne no field `1` on type `&{integer}`. Même cause, mêmes solutions : score est déjà le nombre.
Le résultat de len() ou de count(). let len = scores.len(); vous donne un usize, donc len.count échoue avec `usize` is a primitive type. Correction : utilisez len directement.
Des nombres avec une unité. Si vous voulez écrire price.cents ou distance.meters, créez une petite struct : struct Price { cents: u64 }. Alors price.cents fonctionne, et le type vous empêche de confondre des prix avec d’autres nombres. Voir Les structs.
Erreurs liées
Section intitulée « Erreurs liées »- E0609 : no field
xon typeT: le type n’a pas de champ de ce nom. C’est celle-ci (et pas E0610) que vous obtenez pour des références comme&u32. - E0615 : attempted to take value of method : vous avez écrit
price.roundau lieu deprice.round(). E0689: can't call method on ambiguous numeric type: une méthode sur un nombre dont le type n’est pas encore choisi. Ajoutez un type commei32.- E0599 : no method named x found : la version « méthode », par exemple une méthode que les nombres n’ont pas.
- E0308 : mismatched types : apparaît souvent à côté de E0610 quand une fonction renvoie un nombre alors que vous attendiez une struct.
- Toutes les erreurs du compilateur expliquées
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Après let total = cart.total; (où total est un champ u32), que donne total.amount ?
2. Que veut dire {integer} dans le message ?
3. let x = point.0; puis x.0 donne E0610. Quelle est la correction ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0610 — Rust error codes index (en anglais) : l’explication officielle de cette erreur.
- Data Types — The Rust Book (en anglais) : les types scalaires (entiers, nombres à virgule,
bool,char) et les types composés (tuples, tableaux). - Tuple Structs — The Rust Book (en anglais) : les types enveloppes comme
Meters(u32)et le fonctionnement de.0. - Primitives — Rust by Example (en anglais) : les types intégrés, avec des exemples exécutables.
i32— documentation de la bibliothèque standard (en anglais) : toutes les méthodes d’un entier (et vous verrez qu’elles s’appellent toutes avec()).