Aller au contenu

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.)

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);
}

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.

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.

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"),
email: String::from("[email protected]"), // ✅ ajouté
active: true,
};
println!("{} <{}> actif : {}", user.name, user.email, user.active);
}

Sortie :

Ferris <[email protected]> actif : true
▶ Essayez par vous-même

Solution 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 }
▶ Essayez par vous-même

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).

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 1
Bo : score 50, niveau 1
▶ Essayez par vous-même

Le 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 }
▶ Essayez par vous-même

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.

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"),
email: String::from("[email protected]"),
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 : None
▶ Essayez par vous-même

Remarquez 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.

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.

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