Erreur Rust E0164 : expected tuple struct or tuple variant, found struct variant
E0164 veut dire : vous avez écrit des parenthèses dans un motif (pattern), comme Shape::Circle(r), après un nom qui ne prend pas de valeurs entre parenthèses. Dans le Rust d’aujourd’hui, vous la rencontrerez dans trois situations : une variante d’enum à champs nommés (Circle { radius: f64 }) testée comme si c’était une variante tuple, une fonction associée comme Celsius::new(d) utilisée comme motif, et une constante associée comme Volume::MAX(_) suivie de parenthèses. Chacune a une correction courte et claire. Cette page fait partie du guide des erreurs du compilateur Rust.
Le message d’erreur
Section intitulée « Le message d’erreur »error[E0164]: expected tuple struct or tuple variant, found struct variant `Shape::Circle` --> src/main.rs:8:9 |8 | Shape::Circle(r) => 3.14 * r * r, // ❌ `Circle` a des champs nommés | ^^^^^^^^^^^^^^^^ not a tuple struct or tuple variant |help: the struct variant's field is being ignored |8 - Shape::Circle(r) => 3.14 * r * r, // ❌ `Circle` a des champs nommés8 + Shape::Circle { radius: _ } => 3.14 * r * r, // ❌ `Circle` a des champs nommés |Le compilateur vous dit : « on attendait une struct tuple ou une variante tuple, mais on a trouvé la variante struct Shape::Circle ».
Voici le programme qui la provoque :
enum Shape { Circle { radius: f64 }, Square { side: f64 },}
fn area(shape: &Shape) -> f64 { match shape { Shape::Circle(r) => 3.14 * r * r, // ❌ `Circle` a des champs nommés Shape::Square { side } => side * side, }}
fn main() { println!("{}", area(&Shape::Square { side: 2.0 }));}Ce que ça veut dire
Section intitulée « Ce que ça veut dire »Les ^^^ couvrent tout le motif Shape::Circle(r), et l’étiquette dit ce que Rust attendait à cet endroit : « a tuple struct or tuple variant » (une struct tuple ou une variante tuple). Seules ces deux sortes de choses s’écrivent avec des parenthèses dans un motif, parce que seules elles rangent leurs valeurs par position, comme Some(x) ou Meters(m).
Shape::Circle est une variante struct (struct variant). Sa valeur a un nom, radius, et les champs nommés se testent avec des accolades : Shape::Circle { radius }.
Attention à la ligne help: ici. Elle dit que le champ de la variante struct est ignoré, et propose Shape::Circle { radius: _ }. Ça corrige le motif, mais ça ignore le rayon. Notre branche utilise r, donc copier la suggestion telle quelle donne une nouvelle erreur, E0425 cannot find value `r` in this scope (valeur r introuvable dans cette portée). Quand vous avez besoin de la valeur, donnez-lui un nom entre les accolades, comme dans la solution 1.
C’est comme un formulaire avec des cases étiquetées, « Largeur : … Hauteur : … », où vous auriez écrit « 3, 4 » à côté du titre. Les nombres sont peut-être justes, mais le formulaire veut chacun dans sa case.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »Un motif doit ressembler à la valeur qu’il teste. Rust a deux façons de ranger des données, et chacune a son propre motif (voir Les enums peuvent contenir des données) :
| Définie comme | Créée avec | Testée avec |
|---|---|---|
Circle(f64) (variante tuple) |
Shape::Circle(1.0) |
Shape::Circle(r) |
Circle { radius: f64 } (variante struct) |
Shape::Circle { radius: 1.0 } |
Shape::Circle { radius } |
Les champs nommés se testent par leur nom, pas par leur position : l’ordre dans le motif n’a donc pas d’importance, et ajouter un champ plus tard ne décale pas les autres en douce. Mélanger les deux styles est refusé, ainsi il n’y a jamais de doute sur quelle valeur va où.
Les motifs sont aussi vérifiés à la compilation, donc ils ne peuvent pas exécuter de code. C’est pour ça qu’un nom de fonction suivi de (d) ne peut pas non plus être un motif, même quand la fonction construit exactement la valeur que vous testez. Plus de détails dans Le pattern matching.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : utiliser des accolades avec les noms des champs
Section intitulée « Solution 1 : utiliser des accolades avec les noms des champs »Choisissez-la quand le message dit found struct variant : c’est le E0164 le plus courant. Écrivez le nom du champ (Rust crée alors une variable de ce nom), champ: nouveau_nom pour choisir un autre nom, ou { .. } pour ignorer tous les champs :
enum Shape { Circle { radius: f64 }, Rect { w: f64, h: f64 },}
fn area(shape: &Shape) -> f64 { match shape { Shape::Circle { radius } => 3.14 * radius * radius, // ✅ le nom du champ entre accolades Shape::Rect { w: width, h: height } => width * height, // ✅ champ: nouveau nom }}
fn name(shape: &Shape) -> &'static str { match shape { Shape::Circle { .. } => "cercle", // ✅ « n'importe quel cercle » Shape::Rect { .. } => "rectangle", }}
fn main() { let shapes = [Shape::Circle { radius: 1.0 }, Shape::Rect { w: 2.0, h: 3.0 }]; for shape in &shapes { println!("{} : {:.2}", name(shape), area(shape)); }}Sortie :
cercle : 3.14rectangle : 6.00Si vous listez certains champs mais pas tous, ajoutez .. à la fin, comme Shape::Rect { w, .. }. L’oublier donne E0027, et un nom de champ mal écrit donne E0026.
Solution 2 : transformer la variante en variante tuple
Section intitulée « Solution 2 : transformer la variante en variante tuple »Choisissez-la quand vous voulez vraiment des parenthèses : la variante a une seule valeur évidente, et vous préférez écrire Shape::Circle(1.0) partout. Modifiez la définition, et le motif Circle(r) devient correct :
enum Shape { Circle(f64), // ✅ maintenant une variante tuple : une valeur sans nom Rect { w: f64, h: f64 },}
fn area(shape: &Shape) -> f64 { match shape { Shape::Circle(r) => 3.14 * r * r, // ✅ les parenthèses vont avec une variante tuple Shape::Rect { w, h } => w * h, }}
fn main() { let small = Shape::Circle(1.0); // créée avec des parenthèses aussi let big = Shape::Circle(2.0); let door = Shape::Rect { w: 1.0, h: 2.0 }; println!("{:.2} {:.2} {:.2}", area(&small), area(&big), area(&door));}Sortie :
3.14 12.56 2.00Cela change le type, donc chaque endroit qui crée ou teste Circle doit passer aux parenthèses lui aussi. Le compilateur vous les liste un par un. Pour deux valeurs ou plus, les champs nommés sont en général plus clairs.
Solution 3 : tester la struct tuple elle-même, et appeler les fonctions dans une garde
Section intitulée « Solution 3 : tester la struct tuple elle-même, et appeler les fonctions dans une garde »Choisissez-la quand le message dit found associated function et que l’étiquette dit `fn` calls are not allowed in patterns (les appels de fn ne sont pas permis dans les motifs). Vous avez écrit un constructeur comme Celsius::new(d) là où Rust a besoin d’un motif :
error[E0164]: expected tuple struct or tuple variant, found associated function `Celsius::new` --> src/main.rs:11:9 |11 | Celsius::new(d) if d > 25.0 => "warm", // ❌ `new` est une fonction, pas un motif | ^^^^^^^^^^^^^^^ `fn` calls are not allowed in patternsLe programme qui la provoque :
struct Celsius(f64);
impl Celsius { fn new(degrees: f64) -> Celsius { Celsius(degrees) }}
fn feels(t: Celsius) -> &'static str { match t { Celsius::new(d) if d > 25.0 => "warm", // ❌ `new` est une fonction, pas un motif _ => "cool", }}
fn main() { println!("{}", feels(Celsius::new(30.0)));}Celsius est une struct tuple, donc le motif est le nom de la struct elle-même : Celsius(d). Tout ce qui demande un appel de fonction ou de méthode va dans une garde de match (match guard, le if ... après le motif) :
struct Celsius(f64);
impl Celsius { fn new(degrees: f64) -> Celsius { Celsius(degrees) }
fn is_freezing(&self) -> bool { self.0 <= 0.0 }}
fn feels(t: Celsius) -> &'static str { match t { Celsius(d) if d > 25.0 => "chaud", // ✅ la struct tuple elle-même, plus une garde Celsius(_) => "frais", }}
fn describe(t: &Celsius) -> &'static str { match t { t if t.is_freezing() => "ça gèle", // ✅ les méthodes vont dans une garde _ => "ça ne gèle pas", }}
fn main() { println!("{}", feels(Celsius::new(30.0))); println!("{}", feels(Celsius::new(12.5))); let Celsius(d) = Celsius::new(-3.0); // ✅ marche aussi avec `let` println!("{} degrés", d); println!("{}", describe(&Celsius::new(-3.0)));}Sortie :
chaudfrais-3 degrésça gèleSi le champ de la struct est privé (elle vit dans un autre module), vous ne pouvez pas écrire Celsius(d) en dehors de ce module. Utilisez alors un accesseur (getter) dans une garde, comme t if t.degrees() > 25.0. Voir Les gardes : ajouter un if.
Solution 4 : enlever les parenthèses après une constante
Section intitulée « Solution 4 : enlever les parenthèses après une constante »Choisissez-la quand le message dit found associated constant. Une constante est déjà un motif complet. Elle correspond à une valeur exacte, il n’y a donc rien à mettre entre parenthèses :
error[E0164]: expected tuple struct or tuple variant, found associated constant `Volume::MAX` --> src/main.rs:10:9 |10 | Volume::MAX(_) => "loudest", // ❌ une constante ne prend pas de () | ^^^^^^^^^^^^^^ not a tuple struct or tuple variantLe programme qui la provoque :
struct Volume;
impl Volume { const MAX: u8 = 10;}
fn label(level: u8) -> &'static str { match level { 0 => "muted", Volume::MAX(_) => "loudest", // ❌ une constante ne prend pas de () _ => "normal", }}
fn main() { println!("{}", label(10));}Écrivez la constante toute seule :
struct Volume;
impl Volume { const MAX: u8 = 10;}
fn label(level: u8) -> &'static str { match level { 0 => "muet", Volume::MAX => "au maximum", // ✅ une constante, toute seule 1..=3 => "faible", _ => "normal", }}
fn main() { for level in [0, 2, 7, 10, u8::MAX] { println!("{} : {}", level, label(level)); }}Sortie :
0 : muet2 : faible7 : normal10 : au maximum255 : normalC’est pareil pour les constantes de la bibliothèque standard : u32::MAX(_) donne E0164, u32::MAX est un motif valide.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Après être passé à des champs nommés. Vous avez transformé Move(i32, i32) en Move { x: i32, y: i32 } pour plus de clarté. Chaque ancienne branche Command::Move(x, y) => donne maintenant E0164. Correction : Command::Move { x, y } =>.
Tester avec une fonction « d’analyse ». Quelque chose comme match input { Version::parse(v) => ... }, en espérant que la fonction s’exécute pendant le test. Correction : appelez-la d’abord, puis faites le match sur le résultat : match Version::parse(input) { Some(v) => ..., None => ... }.
Les fonctions libres, les structs simples et les variantes unité donnent plutôt E0532. La même erreur de parenthèses est signalée comme E0532 quand on n’atteint pas le nom à travers un type : une fonction simple (Some(limit()), expected a pattern, found a function call), une struct à champs nommés (let Point(x, y) = p;, found struct `Point` ) ou une variante unité (Status::Active()). Les corrections sont les mêmes que sur cette page.
Erreurs liées
Section intitulée « Erreurs liées »- E0532 : expected tuple struct or tuple variant, found unit variant or struct : la même confusion pour les structs simples, les variantes unité et les fonctions libres, et l’inverse (une variante tuple écrite sans parenthèses).
- E0533 : expected unit struct, unit variant or constant, found struct variant : une variante struct écrite sans aucune accolade, comme
Shape::Circle =>. - E0026 : struct or variant does not have a field named x : les accolades sont bonnes, mais un nom de champ à l’intérieur est faux.
- E0027 : pattern does not mention field x : des accolades qui oublient des champs ; ajoutez
... - E0425 : cannot find value in this scope : ce que vous obtenez si vous copiez la suggestion
{ radius: _ }mais utilisez encorer. - Toutes les erreurs du compilateur : la liste complète des pages d’erreurs.
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. Pour enum Shape { Circle { radius: f64 } }, quelle branche utilise correctement le rayon ?
2. Celsius::new(d) => ... donne « fn calls are not allowed in patterns ». Celsius est struct Celsius(f64);. Quel motif marche ?
3. Comment tester la constante associée Volume::MAX ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0164 — Rust error codes index (en anglais) : l’explication officielle, avec l’exemple de la constante associée.
- Pattern Syntax — The Rust Book (en anglais) : déstructurer des enums avec des accolades ou des parenthèses, ignorer des champs avec
.., et les gardes de match. - Destructuring enums — Rust by Example (en anglais) : variantes tuples et variantes struct testées côte à côte.
- Patterns — The Rust Reference (en anglais) : quels chemins peuvent apparaître dans les motifs de struct tuple et de struct.