Aller au contenu

Erreur Rust E0560 : struct has no field named X (champ inconnu)

E0560 veut dire : vous créez une struct avec Name { field: value }, et l’un des champs que vous avez écrits n’existe pas dans cette struct. En général, c’est une faute de frappe, un champ qui a été renommé, ou un champ qui appartient à une autre struct. rustc vous indique souvent le nom exact que vous vouliez écrire : c’est donc l’une des erreurs les plus rapides à corriger. (Cette page fait partie de Les erreurs du compilateur Rust expliquées.)

error[E0560]: struct `User` has no field named `nmae`
--> src/main.rs:9:9
|
9 | nmae: String::from("Ferris"), // ❌ typo: the field is `name`
| ^^^^ unknown field
|
help: a field with a similar name exists
|
9 - nmae: String::from("Ferris"), // ❌ typo: the field is `name`
9 + name: String::from("Ferris"), // ❌ typo: the field is `name`
|

Le compilateur vous dit : « la struct User n’a pas de champ nommé nmae ». L’étiquette « unknown field » veut dire « champ inconnu », et l’aide « a field with a similar name exists » vous signale qu’un champ au nom proche existe, puis vous montre la ligne corrigée.

Voici le programme qui la provoque :

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
nmae: String::from("Ferris"), // ❌ typo: the field is `name`
email: String::from("[email protected]"),
active: true,
};
println!("{}", user.email);
}

(Le commentaire dit : faute de frappe, le champ s’appelle name.)

Le titre nomme la struct (User) et le champ que vous avez écrit (nmae). Les ^^^^ pointent ce champ à l’intérieur du bloc User { ... }. Rust l’a comparé à la liste des champs de struct User et n’a trouvé aucune correspondance.

Sous la flèche, vous aurez l’un de ces trois indices. Chacun vous apprend quelque chose de différent :

  • help: a field with a similar name exists (un champ au nom proche existe) : une faute de frappe. rustc montre la ligne avec le bon nom.
  • note: available fields are: `username` (champs disponibles : username) : aucun nom proche. Ce sont les champs que vous n’avez pas encore remplis.
  • note: all struct fields are already assigned (tous les champs de la struct sont déjà remplis) : vous avez rempli tous les vrais champs, et celui-ci est en trop.

Remarquez que rustc ne se plaint pas en plus de l’absence de name. Corrigez la faute de frappe, et les deux problèmes disparaissent d’un coup.

C’est comme remplir un formulaire papier et écrire votre numéro de téléphone dans une case « Fax ». Le formulaire n’a pas cette case, alors le guichetier vous le rend.

Les champs d’une struct sont fixés au moment où vous écrivez struct User { ... }. Chaque fois que vous créez un User, Rust vérifie que vous avez donné exactement ces champs : aucun qui manque, aucun en trop, aucun mal écrit. Voir Définir et créer une struct.

Cela attrape de vrais bugs. En JavaScript, { nmae: "Ferris" } crée sans rien dire un objet avec la mauvaise clé, et user.name vaut undefined bien plus loin dans le programme. En Rust, la faute de frappe ne passe pas le compilateur : chaque User de votre programme a donc vraiment un name.

Choisissez-la quand vous voyez a field with a similar name exists. Prenez le nom indiqué dans la ligne help:. Les noms de champs sont sensibles à la casse, et Rust utilise le snake_case : userName et user_name sont donc deux noms différents.

Attention aussi à la syntaxe abrégée d’initialisation (field init shorthand) : User { nmae, email } veut dire User { nmae: nmae, email: email }, le nom de la variable doit donc être le même que celui du champ. Renommez la variable, ou écrivez le champ en entier : User { name: nmae, email }.

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
name: String::from("Ferris"), // ✅ le vrai nom du champ
email: String::from("[email protected]"),
active: true,
};
println!("{} ({}) actif : {}", user.name, user.email, user.active);
// syntaxe abrégée : les noms des variables sont ceux des champs
let name = String::from("Ada");
let email = String::from("[email protected]");
let other = User { name, email, active: false };
println!("{} ({}) actif : {}", other.name, other.email, other.active);
}

Sortie :

Ferris ([email protected]) actif : true
Ada ([email protected]) actif : false
▶ Essayez par vous-même

Solution 2 : utiliser le nouveau nom du champ, ou ajouter le champ

Section intitulée « Solution 2 : utiliser le nouveau nom du champ, ou ajouter le champ »

Choisissez-la quand il n’y a pas d’indice « similar name ». Souvent, la struct a changé : quelqu’un a renommé name en username, ou un champ a été supprimé, mais ce code utilise encore l’ancien. rustc liste ce qui reste :

error[E0560]: struct `User` has no field named `name`
--> src/main.rs:8:9
|
8 | name: String::from("Ferris"),
| ^^^^ `User` does not have this field
|
= note: available fields are: `username`

Ici, l’étiquette dit « User n’a pas ce champ », et la note donne les champs disponibles.

Vous avez deux choix honnêtes. Si la struct est correcte, utilisez le nom actuel du champ. Si c’est votre code qui a raison et que la struct a vraiment besoin de ce champ, ajoutez-le à la définition de la struct. Ici, Config donnait all struct fields are already assigned pour debug, et la solution est d’ajouter debug à la struct :

struct User {
username: String, // renommé depuis `name` : on utilise le nouveau nom
email: String,
}
struct Config {
port: u16,
host: String,
debug: bool, // ajouté à la struct, parce que le code en a besoin
}
fn main() {
let user = User {
username: String::from("ferris"),
email: String::from("[email protected]"),
};
println!("{} ({})", user.username, user.email);
let config = Config {
port: 8080,
host: String::from("localhost"),
debug: true,
};
println!("{}:{} debug={}", config.host, config.port, config.debug);
}

Sortie :

localhost:8080 debug=true
▶ Essayez par vous-même

Si la struct vient d’une crate (bibliothèque) que vous venez de mettre à jour, le champ a peut-être été renommé dans la nouvelle version. Consultez la struct sur docs.rs pour cette version, ou le changelog de la crate. Dès que vous ajoutez un champ à votre propre struct, chaque endroit qui la crée doit aussi le remplir. rustc vous montrera chacun d’eux avec E0063 (voir plus bas).

Choisissez-la quand le champ que vous avez écrit existe, mais dans une autre struct. Avec deux structs qui se ressemblent, comme un Product avec un title et un Customer avec un name, on les confond facilement :

error[E0560]: struct `Product` has no field named `name`
--> src/main.rs:13:9
|
13 | name: String::from("Keyboard"),
| ^^^^ `Product` does not have this field
|
= note: available fields are: `title`

Regardez la note available fields. Si elle liste des champs auxquels vous ne vous attendiez pas, vous êtes en train de construire la mauvaise struct. Utilisez le bon nom de struct, ou le nom de champ de la struct que vous vouliez :

struct Product {
title: String,
price: u32, // en centimes
}
struct Customer {
name: String,
email: String,
}
fn main() {
let keyboard = Product {
title: String::from("clavier"), // les produits ont un `title`
price: 4900,
};
let buyer = Customer {
name: String::from("Ana"), // les clients ont un `name`
email: String::from("[email protected]"),
};
println!("{} achète un {} pour {} centimes", buyer.name, keyboard.title, keyboard.price);
println!("Reçu envoyé à {}", buyer.email);
}

Sortie :

Ana achète un clavier pour 4900 centimes
Reçu envoyé à [email protected]
▶ Essayez par vous-même

Solution 4 : utiliser des parenthèses pour une tuple struct

Section intitulée « Solution 4 : utiliser des parenthèses pour une tuple struct »

Choisissez-la quand la struct est une tuple struct, comme struct Point(i32, i32);. Ses champs n’ont pas de nom, donc Point { x: 1, y: 2 } donne E0560 pour x et pour y, avec help: `Point` is a tuple struct, use the appropriate syntax (Point est une tuple struct, utilisez la syntaxe adaptée) et la suggestion Point(/* i32 */, /* i32 */). Créez-la avec des parenthèses, Point(1, 2), et lisez-la avec .0 et .1. Si vous préférez des noms, changez la définition en struct Point { x: i32, y: i32 }. Voir Tuple ou struct ?

La jumelle de E0560, c’est E0063 : un champ que la struct possède, mais que vous n’avez pas rempli. Rust ne remplit jamais les champs à votre place, chaque champ a donc besoin d’une valeur :

error[E0063]: missing field `active` in initializer of `User`
--> src/main.rs:8:16
|
8 | let user = User {
| ^^^^ missing `active`

Le message veut dire « champ active manquant dans l’initialisation de User ». S’il manque plusieurs champs, il les liste tous : missing fields `active`, `email` and `login_count` in initializer of `User`. Correction : donnez une valeur à chaque champ. Si beaucoup de champs ont une valeur par défaut raisonnable, dérivez Default et utilisez la syntaxe de mise à jour (struct update syntax) .. pour remplir le reste :

#[derive(Debug, Default)]
struct Settings {
volume: u8,
dark_mode: bool,
language: String,
}
fn main() {
// tous les champs écrits en entier
let full = Settings { volume: 80, dark_mode: false, language: String::from("fr") };
// seulement les champs qui vous intéressent, le reste vient de Default
let quick = Settings { dark_mode: true, ..Default::default() };
// on copie le reste depuis une autre valeur
let louder = Settings { volume: 100, ..full };
println!("{:?}", quick);
println!("{:?}", louder);
}

Sortie :

Settings { volume: 0, dark_mode: true, language: "" }
Settings { volume: 100, dark_mode: false, language: "fr" }
▶ Essayez par vous-même

La valeur par défaut d’un nombre est 0, celle d’un bool est false, et celle d’une String est la chaîne vide. Si vous voulez d’autres valeurs par défaut, écrivez vous-même impl Default for Settings. Voir Default : une valeur de départ raisonnable.

Dans la syntaxe de mise à jour ... Settings { dark: true, ..defaults } donne aussi E0560. Ici, la note liste tous les champs (available fields are: `volume`, `dark_mode`, `language`), et il peut n’y avoir aucun indice « similar name » quand les noms sont trop différents. Correction : dark_mode: true.

Les variantes d’enum avec des champs nommés. Pour enum Shape { Circle { radius: f64 } }, écrire Shape::Circle { r: 2.0 } est la même erreur, mais rustc utilise un autre code : E0559, variant `Shape::Circle` has no field named `r` (la variante Shape::Circle n’a pas de champ nommé r), avec la même note available fields et les mêmes indices de faute de frappe. Correction : Shape::Circle { radius: 2.0 }. Voir Les enums peuvent contenir des données.

Une struct venant d’un autre module. Si le champ existe mais n’est pas pub, vous obtenez à la place E0451, « field balance of struct Account is private » (le champ balance de Account est privé). Correction : utilisez le constructeur du module, comme Account::new(...). Voir Tout est privé par défaut.

Petit quiz

1. rustc affiche note: all struct fields are already assigned. Qu'est-ce que ça veut dire ?

2. Vous avez let nmae = ...; et vous écrivez User { nmae, email }. Comment corriger ?

3. Quelle erreur obtenez-vous si vous oubliez un champ, comme User { name, email } sans active ?

Score : 0 / 3