Aller au contenu

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.

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és
8 + 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 }));
}

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.

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.

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.14
rectangle : 6.00
▶ Essayez par vous-même

Si 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.00
▶ Essayez par vous-même

Cela 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 patterns

Le 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 :

chaud
frais
-3 degrés
ça gèle
▶ Essayez par vous-même

Si 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 variant

Le 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 : muet
2 : faible
7 : normal
10 : au maximum
255 : normal
▶ Essayez par vous-même

C’est pareil pour les constantes de la bibliothèque standard : u32::MAX(_) donne E0164, u32::MAX est un motif valide.

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.

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