Erreur Rust E0063 : missing field in initializer (champ manquant)
E0063 veut dire : vous créez une struct avec Name { ... }, mais vous n’avez pas donné de valeur à chacun de ses champs. Rust ne remplit jamais les champs à votre place : chacun a besoin d’une valeur, même si c’est juste 0, false ou None. Le message liste les champs manquants par leur nom, vous savez donc exactement quoi ajouter. C’est l’une des erreurs les plus sympathiques de Rust. (Cette page fait partie de Les erreurs du compilateur Rust expliquées.)
Le message d’erreur
Section intitulée « Le message d’erreur »error[E0063]: missing field `email` in initializer of `User` --> src/main.rs:8:16 |8 | let user = User { | ^^^^ missing `email`Le compilateur vous dit : « champ email manquant dans l’initialisation de User ». L’étiquette « missing email » répète simplement ce qui manque.
Voici le programme qui la provoque :
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { name: String::from("Ferris"), active: true, // ❌ pas de `email` }; println!("{}", user.name);}Ce que ça veut dire
Section intitulée « Ce que ça veut dire »Le titre nomme le champ manquant (email) et la struct (User). Les ^^^^ pointent le nom de la struct, là où vous avez commencé à la construire, et pas une ligne précise : le problème, c’est justement quelque chose qui n’est pas là.
Quand il manque plusieurs champs, rustc les liste par ordre alphabétique. S’il en manque deux ou trois, vous les voyez tous :
error[E0063]: missing fields `debug`, `retries` and `timeout_secs` in initializer of `Config`À partir de quatre, il en montre trois et compte les autres :
error[E0063]: missing fields `debug`, `port`, `retries` and 1 other field in initializer of `Config` --> src/main.rs:10:18 |10 | let config = Config { | ^^^^^^ missing `debug`, `port`, `retries` and 1 other field« 1 other field » veut dire « 1 autre champ » : il en manque encore un. Regardez la définition de struct Config pour voir la liste complète.
C’est comme un formulaire où toutes les cases sont obligatoires. Vous ne pouvez pas le rendre avec la case « e-mail » vide, même si vous comptiez la remplir plus tard.
Pourquoi Rust fait ça
Section intitulée « Pourquoi Rust fait ça »Dans beaucoup de langages, un champ oublié devient discrètement null, 0 ou une chaîne vide. Votre programme tourne alors avec un utilisateur sans e-mail, et plante bien plus loin, souvent en production. Rust n’a pas de null, et il n’invente pas de valeurs dans votre dos. Un User a toujours un vrai email. Voir Définir et créer une struct.
Ça ne veut pas dire que vous devez taper tous les champs à chaque fois. Rust vous donne des raccourcis pour dire « prends les valeurs par défaut » ou « copie le reste depuis cette autre valeur », comme le montrent les solutions ci-dessous. L’important, c’est que ce soit vous qui choisissiez, et que ce choix soit écrit dans le code.
Comment corriger
Section intitulée « Comment corriger »Solution 1 : ajouter le champ manquant
Section intitulée « Solution 1 : ajouter le champ manquant »Choisissez-la la plupart du temps. Le champ existe pour une raison, donnez-lui donc une vraie valeur. Si vous avez une variable qui porte le même nom que le champ, vous pouvez utiliser la syntaxe abrégée User { name, email, active }.
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { name: String::from("Ferris"), active: true, }; println!("{} <{}> actif : {}", user.name, user.email, user.active);}Sortie :
Ferris <[email protected]> actif : trueSolution 2 : dériver Default et utiliser ..Default::default()
Section intitulée « Solution 2 : dériver Default et utiliser ..Default::default() »Choisissez-la quand la struct a beaucoup de champs et que la plupart ont une valeur « vide » évidente, comme des réglages ou des options. Ajoutez #[derive(Default)] à la struct. Ensuite, écrivez seulement les champs qui vous intéressent et terminez par ..Default::default(). Le .. veut dire « prends dans cette valeur tous les champs que je n’ai pas écrits ».
#[derive(Debug, Default)]struct Config { host: String, port: u16, debug: bool, timeout_secs: u64, retries: u8,}
fn main() { let config = Config { host: String::from("localhost"), port: 8080, ..Default::default() // ✅ debug, timeout_secs et retries prennent leur valeur par défaut }; println!("{:?}", config);}Sortie :
Config { host: "localhost", port: 8080, debug: false, timeout_secs: 0, retries: 0 }Les valeurs par défaut dérivées sont 0 pour les nombres, false pour un bool, une String vide et None pour une Option. Si 0 est une mauvaise valeur par défaut (un délai d’attente de 0 seconde l’est sans doute), écrivez vous-même impl Default for Config avec de meilleures valeurs. Voir Default : une valeur de départ raisonnable.
Si vous oubliez le #[derive(Default)], l’erreur devient E0277 : the trait bound `Config: Default` is not satisfied (Config n’implémente pas Default), avec help: consider annotating `Config` with `#[derive(Default)]` (pensez à ajouter #[derive(Default)] à Config).
Solution 3 : écrire un constructeur, new
Section intitulée « Solution 3 : écrire un constructeur, new »Choisissez-la quand vous créez la struct à beaucoup d’endroits, ou quand certains champs commencent toujours de la même façon. Mettez le littéral de struct complet dans une seule fonction, et appelez cette fonction partout ailleurs. Quand vous ajouterez un champ plus tard, vous ne modifierez que new, pas chaque appel.
struct Player { name: String, score: u32, level: u8,}
impl Player { fn new(name: &str) -> Player { Player { name: name.to_string(), score: 0, // chaque nouveau joueur commence à 0... level: 1, // ...au niveau 1 } }}
fn main() { let ana = Player::new("Ana"); let mut bo = Player::new("Bo"); bo.score += 50; println!("{} : score {}, niveau {}", ana.name, ana.score, ana.level); println!("{} : score {}, niveau {}", bo.name, bo.score, bo.level);}Sortie :
Ana : score 0, niveau 1Bo : score 50, niveau 1Le littéral de struct dans new a toujours besoin de tous les champs : un champ oublié y apparaîtra donc sous forme d’E0063. Ce n’est pas grave, vous ne le corrigez qu’une fois. Voir Les fonctions de type constructeur.
Solution 4 : copier le reste depuis une autre valeur avec ..other
Section intitulée « Solution 4 : copier le reste depuis une autre valeur avec ..other »Choisissez-la quand vous avez déjà une valeur proche et que vous voulez une copie avec quelques changements. ..base prend dans base tous les champs que vous n’avez pas écrits :
#[derive(Debug, Clone)]struct Theme { name: String, font_size: u8, dark: bool,}
fn main() { let base = Theme { name: String::from("Standard"), font_size: 14, dark: false };
// seuls `name` et `dark` changent, le reste vient de `base` let night = Theme { name: String::from("Nuit"), dark: true, ..base };
// `..base` déplacerait `name` hors de base : on clone pour pouvoir encore utiliser base let large = Theme { font_size: 20, ..base.clone() };
println!("{:?}", night); println!("{:?}", large); println!("{:?}", base);}Sortie :
Theme { name: "Nuit", font_size: 14, dark: true }Theme { name: "Standard", font_size: 20, dark: false }Theme { name: "Standard", font_size: 14, dark: false }Une chose à savoir : ..base déplace (move) les champs comme String hors de base. Pour night, ce n’est pas un problème, car nous avons écrit notre propre name : seuls les champs Copy (font_size) sont venus de base. Pour large, name viendrait de base, alors nous utilisons ..base.clone() pour que base reste utilisable. Si vous oubliez le clone et utilisez base ensuite, vous obtenez E0382.
Quand des champs Option vous aident
Section intitulée « Quand des champs Option vous aident »Parfois, un champ peut vraiment être vide : un numéro de téléphone que l’utilisateur n’a pas encore donné, un surnom, une date de fin. Faites de ce champ une Option. Vous devez toujours l’écrire (Rust ne met pas None à votre place), mais None est maintenant une valeur honnête et autorisée. Voir Option.
#[derive(Debug, Default)]struct Contact { name: String, email: String, phone: Option<String>, // peut manquer nickname: Option<String>,}
fn main() { let a = Contact { name: String::from("Ana"), phone: None, // écrit en entier : « pas encore de téléphone » nickname: Some(String::from("Annie")), }; // avec Default, chaque Option que vous ne remplissez pas devient None let b = Contact { name: String::from("Bo"), ..Default::default() };
println!("{} téléphone : {:?}, surnom : {:?}", a.name, a.phone, a.nickname); println!("{} e-mail : {:?}, téléphone : {:?}", b.name, b.email, b.phone);}Sortie :
Ana téléphone : None, surnom : Some("Annie")Bo e-mail : "", téléphone : NoneRemarquez que b.email vaut "". C’est le piège de Default : un champ obligatoire devient vide sans prévenir. Réservez Option aux choses vraiment facultatives, et gardez les champs obligatoires comme email avec des types simples que vous remplissez toujours.
Où vous le verrez souvent
Section intitulée « Où vous le verrez souvent »Juste après avoir ajouté un champ à une struct. Vous ajoutez created_at à User, et d’un coup chaque User { ... } du projet donne E0063. C’est Rust qui vous aide : chacun de ces endroits mérite que vous y réfléchissiez. Un constructeur new (solution 3) garde cette liste courte.
Les variantes d’enum à champs nommés. Shape::Rectangle { width: 2.0 } donne missing field `height` in initializer of `Shape`. Remarquez que le message nomme l’enum, pas la variante. Correction : donnez une valeur à chaque champ de cette variante. Voir Les enums peuvent contenir des données.
Une fonction qui renvoie une struct. Dans fn new_player(name: &str) -> Player { Player { name: name.to_string(), score: 0 } }, la flèche pointe Player à l’intérieur de la fonction, avec missing `level`. Correction : ajoutez level à ce littéral.
Erreurs liées
Section intitulée « Erreurs liées »- E0560 : struct has no field named x : l’inverse, un champ que vous avez écrit mais que la struct n’a pas. Une faute de frappe donne souvent les deux à la fois.
- E0559 : variant has no field named x : la même erreur de nom pour les variantes d’enum.
- E0277 : trait bound not satisfied : vous avez écrit
..Default::default(), mais la struct ne dérive pasDefault. - E0451 : field is private : la struct vient d’un autre module et certains champs sont privés, vous ne pouvez donc pas les remplir. Si vous omettez un champ privé, vous n’obtenez pas E0063 mais « cannot construct
Accountwith struct literal syntax due to private fields » (impossible de construireAccountavec un littéral de struct à cause de champs privés). Dans les deux cas, utilisez la fonctionnewdu module. - E0382 : use of moved value : vous avez utilisé une valeur après que
..valueen a déplacé les champs. - Toutes les erreurs du compilateur expliquées
Petit quiz
Section intitulée « Petit quiz »Petit quiz
1. rustc dit : missing fields a, b, c and 2 other fields. Combien de champs manque-t-il ?
2. Votre struct a un champ Option<String> dont vous n'avez pas encore besoin. Qu'écrivez-vous pour ce champ ?
3. De quoi Config { port: 80, ..Default::default() } a-t-il besoin pour compiler ?
Score : 0 / 3
Pour aller plus loin
Section intitulée « Pour aller plus loin »- E0063 — Rust error codes index (en anglais) : l’explication officielle de cette erreur.
- Defining and Instantiating Structs — The Rust Book (en anglais) : les littéraux de struct, la syntaxe abrégée d’initialisation et la syntaxe de mise à jour.
Default— documentation de la bibliothèque standard (en anglais) :#[derive(Default)], écrire votre propreimpl Default, et..Default::default().- Struct expressions — The Rust Reference (en anglais) : les règles exactes de
Name { field: value }et de..base. - Constructors — Rust API Guidelines (en anglais) : pourquoi le code Rust utilise des fonctions
newpour construire les structs.