Aller au contenu

Erreur Rust E0026 : struct or variant does not have a field named X (champ dans un motif)

E0026 veut dire que vous décomposez une struct ou une variante d’enum avec un motif (pattern), comme Shape::Circle { r }, et qu’un des noms de champs que vous avez écrits n’existe pas. C’est la version « motif » de E0560 et de E0559, qui attrapent la même erreur quand vous créez une valeur. rustc vous donne souvent le bon nom dans une ligne help:, et la correction tient alors en un seul mot. Cette page fait partie du guide des erreurs du compilateur Rust.

error[E0026]: variant `Shape::Circle` does not have a field named `r`
--> src/main.rs:8:25
|
8 | Shape::Circle { r } => 3.14 * r * r, // ❌ le champ s'appelle `radius`
| ^
| |
| variant `Shape::Circle` does not have this field
| help: `Shape::Circle` has a field named `radius`

Le compilateur vous dit : « la variante Shape::Circle n’a pas de champ nommé r ».

Voici le programme qui la provoque :

enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { r } => 3.14 * r * r, // ❌ le champ s'appelle `radius`
Shape::Rectangle { width, height } => width * height,
}
}
fn main() {
println!("{}", area(&Shape::Circle { radius: 1.0 }));
}

Vous verrez aussi une deuxième erreur sur la même ligne : E0027, « pattern does not mention field radius » (le motif ne mentionne pas le champ radius). C’est la même erreur, vue de l’autre côté. Rust a trouvé un champ que vous avez nommé mais qui n’existe pas (r), et un vrai champ que vous n’avez pas nommé (radius). Corrigez le nom, et les deux erreurs disparaissent.

Le titre de l’erreur donne la struct ou la variante que vous filtrez (Shape::Circle) et le champ que vous avez écrit (r). Le ^ pointe ce nom, entre les accolades du motif.

Dans un motif, les noms entre { } sont des noms de champs, pas de nouveaux noms de variables que vous choisissez librement. Shape::Circle { r } est un raccourci pour Shape::Circle { r: r } : « prendre le champ appelé r et le mettre dans une variable appelée r ». Circle n’a pas de champ r, alors Rust s’arrête.

Sous la flèche, rustc donne une de ces indications :

  • help: `Shape::Circle` has a field named `radius` (Shape::Circle a un champ nommé radius) : il reste exactement un champ que vous n’avez pas mentionné, alors rustc suppose que c’est celui-là que vous vouliez.
  • help: a field with a similar name exists (un champ au nom proche existe) : une faute de frappe, comme nmae au lieu de name. rustc affiche la ligne corrigée.
  • Aucune indication : rustc n’a pas su deviner. Regardez la définition de la struct ou de l’enum pour trouver les vrais noms.

C’est comme demander à un bibliothécaire « le livre sur l’étagère Romans-un-peu ». Il comprend que vous voulez une étagère, mais il ne peut aller qu’à celles qui existent vraiment.

Un motif comme Shape::Circle { radius } est une promesse : « quand la valeur est un Circle, donne-moi son radius ». Rust vérifie cette promesse par rapport à la définition au moment de la compilation, si bien qu’un motif ne peut jamais réclamer une donnée qui n’existe pas. Voir Déstructurer des tuples et des structs.

Dans les langages sans cette vérification, lire une clé absente vous donne undefined ou null, et le bug n’apparaît que bien plus tard, loin de la faute de frappe. En Rust, la faute de frappe ne passe pas le compilateur. Ça vous protège aussi quand quelqu’un renomme un champ : chaque motif qui utilise encore l’ancien nom s’allume avec E0026, et aucun n’est oublié en silence.

Choisissez-la la plupart du temps. Prenez le nom indiqué dans la ligne help:, ou regardez la définition. Les noms de champs respectent la casse et s’écrivent en snake_case.

Une faute de frappe donne l’indication « similar name » et la ligne corrigée :

error[E0026]: struct `User` does not have a field named `nmae`
--> src/main.rs:8:16
|
8 | let User { nmae, age } = user; // ❌ faute de frappe : le champ est `name`
| ^^^^ struct `User` does not have this field
|
help: a field with a similar name exists
|
8 - let User { nmae, age } = user; // ❌ faute de frappe : le champ est `name`
8 + let User { name, age } = user; // ❌ faute de frappe : le champ est `name`
|

Le programme qui la provoque :

struct User {
name: String,
age: u32,
}
fn main() {
let user = User { name: String::from("Ferris"), age: 7 };
let User { nmae, age } = user; // ❌ faute de frappe : le champ est `name`
println!("{} a {} ans", nmae, age);
}

Écrivez les noms de champs exactement comme dans la définition. Le raccourci marche alors, et vous obtenez des variables qui portent les mêmes noms :

struct User {
name: String,
age: u32,
}
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { radius } => 3.14 * radius * radius, // ✅ le vrai nom du champ
Shape::Rectangle { width, height } => width * height,
}
}
fn main() {
let shapes = [
Shape::Circle { radius: 2.0 },
Shape::Rectangle { width: 3.0, height: 4.0 },
];
for shape in &shapes {
println!("aire : {:.2}", area(shape));
}
let user = User { name: String::from("Ferris"), age: 7 };
let User { name, age } = user; // ✅ `name`, pas `nmae`
println!("{} a {} ans", name, age);
}

Sortie :

aire : 12.56
aire : 12.00
Ferris a 7 ans
▶ Essayez par vous-même

Solution 2 : garder votre propre nom de variable avec champ: nom

Section intitulée « Solution 2 : garder votre propre nom de variable avec champ: nom »

Choisissez-la quand vous aimez le nom court r et voulez juste que la variable s’appelle ainsi. Écrivez le vrai nom du champ, deux-points, puis le nom de votre variable : Shape::Circle { radius: r }. Lisez-le comme « prendre le champ radius et l’appeler r ».

enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
}
fn area(shape: &Shape) -> f64 {
match shape {
Shape::Circle { radius: r } => 3.14 * r * r, // ✅ champ `radius`, variable `r`
Shape::Rectangle { width: w, height: h } => w * h,
}
}
fn main() {
let small = Shape::Circle { radius: 1.0 };
let wide = Shape::Rectangle { width: 10.0, height: 2.5 };
println!("petit : {:.2}", area(&small));
println!("large : {:.2}", area(&wide));
}

Sortie :

petit : 3.14
large : 25.00
▶ Essayez par vous-même

C’est aussi pratique quand deux champs entreraient sinon en conflit avec des variables que vous avez déjà, ou quand le nom du champ est long.

Solution 3 : vérifier que vous filtrez la bonne variante

Section intitulée « Solution 3 : vérifier que vous filtrez la bonne variante »

Choisissez-la quand le champ que vous avez écrit existe bien, mais dans une autre variante. Chaque variante d’un enum a ses propres champs. Ici, x appartient à Click, mais la branche filtre KeyPress :

error[E0026]: variant `Event::KeyPress` does not have a field named `x`
--> src/main.rs:9:27
|
9 | Event::KeyPress { x, .. } => format!("touche en {}", x), // ❌ `x` appartient à `Click`
| ^
| |
| variant `Event::KeyPress` does not have this field
| help: `Event::KeyPress` has a field named `key`

Le programme qui la provoque :

enum Event {
Click { x: i32, y: i32 },
KeyPress { key: char },
}
fn describe(event: &Event) -> String {
match event {
Event::Click { x, y } => format!("clic en ({}, {})", x, y),
Event::KeyPress { x, .. } => format!("touche en {}", x), // ❌ `x` appartient à `Click`
}
}
fn main() {
println!("{}", describe(&Event::KeyPress { key: 'a' }));
}

La suggestion de rustc (key) veut seulement dire « le seul champ que vous n’avez pas mentionné », alors ne la suivez pas les yeux fermés. Demandez-vous ce que cette branche doit faire. Une touche pressée n’a pas de position, donc ici la bonne correction est d’utiliser le champ que KeyPress possède vraiment :

enum Event {
Click { x: i32, y: i32 },
KeyPress { key: char },
}
fn describe(event: &Event) -> String {
match event {
Event::Click { x, y } => format!("clic en ({}, {})", x, y),
Event::KeyPress { key } => format!("touche '{}' pressée", key), // ✅ KeyPress a `key`
}
}
fn main() {
let events = [Event::Click { x: 10, y: 20 }, Event::KeyPress { key: 'a' }];
for event in &events {
println!("{}", describe(event));
}
}

Sortie :

clic en (10, 20)
touche 'a' pressée
▶ Essayez par vous-même

Si une touche pressée doit vraiment avoir une position, ajoutez plutôt les champs à la variante : KeyPress { key: char, x: i32, y: i32 }. Chaque endroit qui crée un KeyPress devra alors les remplir aussi (rustc les pointe un par un avec E0063).

Après avoir renommé un champ. Vous avez renommé r en radius dans l’enum, et chaque branche Shape::Circle { r } => ... donne maintenant E0026. Cherchez l’ancien nom dans le projet et mettez à jour chaque motif, ou écrivez radius: r pour garder l’ancien nom de variable.

Dans if let et let. if let Some(User { nmae, .. }) = find_user() donne la même erreur. Ajouter .. n’aide pas ici : .. saute les champs que vous ne nommez pas, mais chaque nom que vous écrivez doit quand même exister. Correction : User { name, .. }.

Des accolades sur une variante tuple ou une struct tuple. Pour struct Point(i32, i32);, le motif Point { x, y } n’a pas de vrais noms de champs à faire correspondre. rustc le signale avec un autre code, E0769, « tuple variant Point written as struct variant » (variante tuple Point écrite comme une variante struct), et propose Point(x, y). Voir Déstructurer des enums qui portent des données.

Petit quiz

1. Avec enum Shape { Circle { radius: f64 } }, que demande le motif Shape::Circle { r } ?

2. Vous voulez le champ radius dans une variable appelée r. Quel motif marche ?

3. User { nmae, .. } compile-t-il si le champ s'appelle name ?

Score : 0 / 3