Error E0063 de Rust: missing field in initializer (falta un campo)
E0063 significa: estás creando un struct con Nombre { ... }, pero no le diste un valor a cada campo. Rust nunca rellena campos por ti, así que cada uno necesita un valor, aunque sea solo 0, false o None. El mensaje enumera por su nombre los campos que faltan, así que sabes exactamente qué añadir. Es uno de los errores más amables de Rust. (Esta página forma parte de Errores del compilador de Rust explicados.)
El mensaje de error
Sección titulada «El mensaje de error»error[E0063]: missing field `email` in initializer of `User` --> src/main.rs:8:16 |8 | let user = User { | ^^^^ missing `email`“Missing field email in initializer of User” quiere decir “falta el campo email al inicializar User”. La etiqueta missing `email` repite lo mismo: falta email.
Este es el programa que lo produce:
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { name: String::from("Ferris"), active: true, // ❌ no `email` }; println!("{}", user.name);}Qué significa
Sección titulada «Qué significa»El titular nombra el campo que falta (email) y el struct (User). Los ^^^^ señalan el nombre del struct, donde empezaste a construirlo, y no una línea concreta, porque el problema es algo que no está.
Cuando faltan más campos, rustc los enumera en orden alfabético. Si son dos o tres, los ves todos:
error[E0063]: missing fields `debug`, `retries` and `timeout_secs` in initializer of `Config`Si son cuatro o más, te enseña tres y cuenta el resto:
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” significa que falta uno más. Mira la definición de struct Config para ver la lista completa.
Es como un formulario en el que todas las casillas son obligatorias. No puedes entregarlo con la casilla del “correo” vacía, aunque pensaras rellenarla más tarde.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»En muchos lenguajes, un campo que falta se convierte sin avisar en null, 0 o una cadena vacía. Entonces tu programa funciona con un usuario que no tiene correo, y se rompe en algún sitio muy lejano, a menudo ya en producción. Rust no tiene null, y no se inventa valores a tus espaldas. Un User siempre tiene un email de verdad. Mira Definir y crear un struct.
Eso no significa que tengas que escribir todos los campos cada vez. Rust te da formas cortas de decir “usa los valores por defecto” o “copia el resto de este otro valor”, como verás en las soluciones de abajo. Lo importante es que eliges tú, y esa decisión queda escrita en el código.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Añade el campo que falta
Sección titulada «Solución 1: Añade el campo que falta»Elige esta la mayoría de las veces. El campo está ahí por algo, así que dale un valor real. Si tienes una variable con el mismo nombre que el campo, puedes usar la forma abreviada User { name, email, active }.
struct User { name: String, email: String, active: bool,}
fn main() { let user = User { name: String::from("Ferris"), active: true, }; println!("{} <{}> activo: {}", user.name, user.email, user.active);}Salida:
Ferris <[email protected]> activo: trueSolución 2: Deriva Default y usa ..Default::default()
Sección titulada «Solución 2: Deriva Default y usa ..Default::default()»Elige esta cuando el struct tiene muchos campos y la mayoría tienen un valor “vacío” obvio, como una configuración o unas opciones. Añade #[derive(Default)] al struct. Después escribe solo los campos que te interesan y termina con ..Default::default(). El .. significa “toma de este valor todos los campos que no he escrito”.
#[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 y retries reciben su valor por defecto }; println!("{:?}", config);}Salida:
Config { host: "localhost", port: 8080, debug: false, timeout_secs: 0, retries: 0 }Los valores por defecto derivados son 0 para los números, false para bool, un String vacío y None para un Option. Si 0 es un mal valor por defecto (un tiempo de espera de 0 segundos probablemente lo es), escribe tú mismo impl Default for Config con valores mejores. Mira Default: un valor inicial razonable.
Si se te olvida el #[derive(Default)], el error pasa a ser E0277: the trait bound `Config: Default` is not satisfied (no se cumple el trait bound Config: Default), con help: consider annotating `Config` with `#[derive(Default)]` (prueba a anotar Config con #[derive(Default)]).
Solución 3: Escribe un constructor, new
Sección titulada «Solución 3: Escribe un constructor, new»Elige esta cuando creas el struct en muchos sitios, o cuando algunos campos siempre empiezan igual. Pon el literal completo del struct en una sola función, y llama a esa función en todos los demás sitios. Cuando más adelante añadas un campo, solo tendrás que actualizar new, no cada llamada.
struct Player { name: String, score: u32, level: u8,}
impl Player { fn new(name: &str) -> Player { Player { name: name.to_string(), score: 0, // cada jugador nuevo empieza con 0 puntos... level: 1, // ...en el nivel 1 } }}
fn main() { let ana = Player::new("Ana"); let mut bo = Player::new("Bo"); bo.score += 50; println!("{}: puntos {}, nivel {}", ana.name, ana.score, ana.level); println!("{}: puntos {}, nivel {}", bo.name, bo.score, bo.level);}Salida:
Ana: puntos 0, nivel 1Bo: puntos 50, nivel 1El literal del struct dentro de new sigue necesitando todos los campos, así que si olvidas uno, el E0063 aparece ahí. No pasa nada: lo arreglas una sola vez. Mira Funciones constructoras.
Solución 4: Copia el resto de otro valor con ..other
Sección titulada «Solución 4: Copia el resto de otro valor con ..other»Elige esta cuando ya tienes un valor parecido y quieres una copia con algunos cambios. ..base toma de base todos los campos que no escribiste:
#[derive(Debug, Clone)]struct Theme { name: String, font_size: u8, dark: bool,}
fn main() { let base = Theme { name: String::from("Default"), font_size: 14, dark: false };
// solo cambian `name` y `dark`, el resto sale de `base` let night = Theme { name: String::from("Night"), dark: true, ..base };
// `..base` movería `name` fuera de base, así que lo clonamos para seguir usando base let large = Theme { font_size: 20, ..base.clone() };
println!("{:?}", night); println!("{:?}", large); println!("{:?}", base);}Salida:
Theme { name: "Night", font_size: 14, dark: true }Theme { name: "Default", font_size: 20, dark: false }Theme { name: "Default", font_size: 14, dark: false }Algo que conviene saber: ..base mueve fuera de base los campos como String. En night no hay problema, porque escribimos nuestro propio name, así que de base solo salió un campo Copy (font_size). En large, name vendría de base, así que usamos ..base.clone() para poder seguir usando base. Si te saltas el clon y usas base después, obtienes E0382.
Cuando los campos Option ayudan
Sección titulada «Cuando los campos Option ayudan»A veces un campo de verdad puede estar vacío: un teléfono que el usuario todavía no ha dado, un apodo, una fecha de fin. Haz que ese campo sea un Option. Sigues teniendo que escribirlo (Rust no rellena None por ti), pero ahora None es un valor honesto y permitido. Mira Option.
#[derive(Debug, Default)]struct Contact { name: String, email: String, phone: Option<String>, // puede faltar nickname: Option<String>,}
fn main() { let a = Contact { name: String::from("Ana"), phone: None, // escrito a mano: "todavía no hay teléfono" nickname: Some(String::from("Anita")), }; // con Default, cada Option que no escribes se convierte en None let b = Contact { name: String::from("Bo"), ..Default::default() };
println!("{} teléfono: {:?}, apodo: {:?}", a.name, a.phone, a.nickname); println!("{} correo: {:?}, teléfono: {:?}", b.name, b.email, b.phone);}Salida:
Ana teléfono: None, apodo: Some("Anita")Bo correo: "", teléfono: NoneFíjate en que b.email es "". Esa es la trampa de Default: un campo obligatorio se queda vacío sin avisar. Usa Option solo para lo que de verdad es opcional, y deja los campos obligatorios como email con tipos normales que siempre rellenas.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»Justo después de añadir un campo a un struct. Añades created_at a User, y de repente cada User { ... } del proyecto da E0063. Es Rust ayudándote: cada uno de esos sitios es uno en el que tienes que pensar. Un constructor new (Solución 3) hace que esta lista sea corta.
Variantes de enum con campos con nombre. Shape::Rectangle { width: 2.0 } da missing field `height` in initializer of `Shape` (falta el campo height al inicializar Shape). Fíjate en que el mensaje nombra el enum, no la variante. Solución: dale un valor a cada campo de esa variante. Mira Los enums pueden guardar datos.
Una función que devuelve un struct. En fn new_player(name: &str) -> Player { Player { name: name.to_string(), score: 0 } }, la flecha señala Player dentro de la función, con missing `level`. Solución: añade level a ese literal.
Errores relacionados
Sección titulada «Errores relacionados»- E0560: struct has no field named x: lo contrario, un campo que escribiste y que el struct no tiene. Un error al escribir a menudo provoca los dos a la vez.
- E0559: variant has no field named x: el mismo error de nombre equivocado, pero en variantes de enum.
- E0277: trait bound not satisfied: escribiste
..Default::default()pero el struct no derivaDefault. - E0451: field is private: el struct viene de otro módulo y algunos campos son privados, así que no puedes rellenarlos. Si dejas fuera un campo privado, no obtienes E0063 sino “cannot construct
Accountwith struct literal syntax due to private fields” (no se puede construirAccountcon un literal de struct porque tiene campos privados). En los dos casos, usa la funciónnewdel módulo. - E0382: use of moved value: usaste un valor después de que
..valormoviera sus campos fuera. - Todos los errores del compilador: la lista completa de páginas de errores.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. rustc dice: missing fields a, b, c and 2 other fields. ¿Cuántos campos faltan?
2. Tu struct tiene un campo Option<String> que todavía no necesitas. ¿Qué escribes en él?
3. ¿Qué necesita Config { port: 80, ..Default::default() } para compilar?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0063 — Rust error codes index (en inglés): la explicación oficial de este error.
- Defining and Instantiating Structs — El libro de Rust (en inglés): los literales de struct, la forma abreviada de inicialización de campos y la sintaxis de actualización. También existe una traducción al español de la comunidad del libro.
Default— documentación de la biblioteca estándar (en inglés):#[derive(Default)], cómo escribir tu propioimpl Defaulty..Default::default().- Struct expressions — The Rust Reference (en inglés): las reglas exactas de
Nombre { campo: valor }y..base. - Constructors — Rust API Guidelines (en inglés): por qué el código de Rust usa funciones
newpara construir structs.