Pular para o conteúdo

Erro E0063 do Rust: missing field in initializer (campo faltando)

O E0063 quer dizer: você está criando uma struct com Nome { ... }, mas não deu um valor para todos os campos. O Rust nunca preenche campos por você, então cada um precisa de um valor, nem que seja 0, false ou None. A mensagem diz o nome dos campos que faltam, então você sabe exatamente o que acrescentar. Este é um dos erros mais amigáveis do Rust. (Esta página faz parte de Erros do compilador Rust explicados.)

error[E0063]: missing field `email` in initializer of `User`
--> src/main.rs:8:16
|
8 | let user = User {
| ^^^^ missing `email`

Em português: “falta o campo email na inicialização de User”. O rótulo repete: “falta email”.

Este é o programa que gera o erro:

struct User {
name: String,
email: String,
active: bool,
}
fn main() {
let user = User {
name: String::from("Ferris"),
active: true,
// ❌ sem `email`
};
println!("{}", user.name);
}

A primeira linha diz o nome do campo que falta (email) e da struct (User). O ^^^^ aponta para o nome da struct, onde você começou a montá-la, e não para uma linha específica, porque o problema é algo que não está ali.

Quando faltam mais campos, o rustc lista todos em ordem alfabética. Com dois ou três, você vê todos:

error[E0063]: missing fields `debug`, `retries` and `timeout_secs` in initializer of `Config`

Com quatro ou mais, ele mostra três e conta o 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” quer dizer que falta mais um campo. Olhe a definição de struct Config para ver a lista completa.

É como um formulário em que todos os campos são obrigatórios. Você não pode entregá-lo com o campo “e-mail” em branco, mesmo que pretendesse preencher depois.

Em muitas linguagens, um campo que falta vira null, 0 ou uma string vazia sem ninguém avisar. Aí o seu programa roda com um usuário sem e-mail, e quebra bem longe dali, muitas vezes em produção. O Rust não tem null e não inventa valores pelas suas costas. Um User sempre tem um email de verdade. Veja Definindo e criando uma struct.

Isso não quer dizer que você precisa digitar todos os campos toda vez. O Rust tem jeitos curtos de dizer “use os valores padrão” ou “copie o resto deste outro valor”, como mostram as correções abaixo. A questão é que você escolhe, e a escolha fica escrita no código.

Escolha esta na maioria das vezes. O campo existe por um motivo, então dê um valor de verdade para ele. Se você tem uma variável com o mesmo nome do campo, pode usar a forma abreviada 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]"), // ✅ acrescentado
active: true,
};
println!("{} <{}> ativo: {}", user.name, user.email, user.active);
}

Saída:

Ferris <[email protected]> ativo: true
▶ Experimente você mesmo

Correção 2: Derive Default e use ..Default::default()

Seção intitulada “Correção 2: Derive Default e use ..Default::default()”

Escolha esta quando a struct tem muitos campos e a maioria deles tem um valor “vazio” óbvio, como configurações ou opções. Acrescente #[derive(Default)] à struct. Depois escreva só os campos que importam e termine com ..Default::default(). O .. quer dizer “pegue deste valor todos os campos que eu não escrevi”.

#[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 e retries recebem o padrão
};
println!("{:?}", config);
}

Saída:

Config { host: "localhost", port: 8080, debug: false, timeout_secs: 0, retries: 0 }
▶ Experimente você mesmo

Os padrões derivados são 0 para números, false para bool, uma String vazia e None para um Option. Se 0 é um padrão ruim (um timeout de 0 segundos provavelmente é), escreva você mesmo um impl Default for Config com valores melhores. Veja Default: um valor inicial sensato.

Se você esquecer o #[derive(Default)], o erro vira E0277: the trait bound `Config: Default` is not satisfied (o trait Default não está implementado para Config), com help: consider annotating `Config` with `#[derive(Default)]` (considere anotar Config com #[derive(Default)]).

Escolha esta quando você cria a struct em vários lugares, ou quando alguns campos sempre começam do mesmo jeito. Coloque a struct completa numa função só e chame essa função em todos os outros lugares. Quando você acrescentar um campo depois, só precisa atualizar o new, e não cada chamada.

struct Player {
name: String,
score: u32,
level: u8,
}
impl Player {
fn new(name: &str) -> Player {
Player {
name: name.to_string(),
score: 0, // todo jogador novo começa com 0...
level: 1, // ...no nível 1
}
}
}
fn main() {
let ana = Player::new("Ana");
let mut bo = Player::new("Bo");
bo.score += 50;
println!("{}: {} pontos, nível {}", ana.name, ana.score, ana.level);
println!("{}: {} pontos, nível {}", bo.name, bo.score, bo.level);
}

Saída:

Ana: 0 pontos, nível 1
Bo: 50 pontos, nível 1
▶ Experimente você mesmo

A struct dentro do new continua precisando de todos os campos, então um campo esquecido aparece ali como E0063. Tudo bem: você corrige uma vez só. Veja Funções construtoras.

Correção 4: Copie o resto de outro valor com ..outro

Seção intitulada “Correção 4: Copie o resto de outro valor com ..outro”

Escolha esta quando você já tem um valor parecido e quer uma cópia com algumas mudanças. ..base pega de base todos os campos que você não escreveu:

#[derive(Debug, Clone)]
struct Theme {
name: String,
font_size: u8,
dark: bool,
}
fn main() {
let base = Theme { name: String::from("Padrão"), font_size: 14, dark: false };
// só `name` e `dark` mudam, o resto vem de `base`
let night = Theme { name: String::from("Noturno"), dark: true, ..base };
// `..base` moveria `name` para fora de base, então clone para continuar usando base
let large = Theme { font_size: 20, ..base.clone() };
println!("{:?}", night);
println!("{:?}", large);
println!("{:?}", base);
}

Saída:

Theme { name: "Noturno", font_size: 14, dark: true }
Theme { name: "Padrão", font_size: 20, dark: false }
Theme { name: "Padrão", font_size: 14, dark: false }
▶ Experimente você mesmo

Uma coisa importante: ..base move campos como String para fora de base. Em night não tem problema, porque escrevemos o nosso próprio name, então só os campos Copy (font_size) vieram de base. Em large, o name viria de base, então usamos ..base.clone() para base continuar utilizável. Se você pular o clone e usar base depois, recebe o E0382.

Às vezes um campo pode mesmo ficar vazio: um telefone que o usuário ainda não informou, um apelido, uma data de término. Faça esse campo ser um Option. Você ainda precisa escrevê-lo (o Rust não preenche None por você), mas agora None é um valor honesto e permitido. Veja Option.

#[derive(Debug, Default)]
struct Contact {
name: String,
email: String,
phone: Option<String>, // pode faltar
nickname: Option<String>,
}
fn main() {
let a = Contact {
name: String::from("Ana"),
email: String::from("[email protected]"),
phone: None, // escrito por extenso: "ainda sem telefone"
nickname: Some(String::from("Aninha")),
};
// com Default, todo Option que você deixar de fora vira None
let b = Contact { name: String::from("Bo"), ..Default::default() };
println!("{} telefone: {:?}, apelido: {:?}", a.name, a.phone, a.nickname);
println!("{} e-mail: {:?}, telefone: {:?}", b.name, b.email, b.phone);
}

Saída:

Ana telefone: None, apelido: Some("Aninha")
Bo e-mail: "", telefone: None
▶ Experimente você mesmo

Repare que b.email é "". Essa é a armadilha do Default: um campo obrigatório vira vazio sem avisar. Use Option só para coisas que são opcionais de verdade, e mantenha campos obrigatórios como email em tipos simples que você sempre preenche.

Logo depois de acrescentar um campo a uma struct. Você acrescenta created_at a User, e de repente todo User { ... } do projeto dá E0063. É o Rust ajudando você: cada um desses lugares é um ponto em que você precisa pensar. Um construtor new (Correção 3) deixa essa lista curta.

Variantes de enum com campos nomeados. Shape::Rectangle { width: 2.0 } dá missing field `height` in initializer of `Shape`. Repare que a mensagem diz o nome do enum, e não da variante. Correção: dê um valor para cada campo daquela variante. Veja Enums podem guardar dados.

Uma função que retorna uma struct. Em fn new_player(name: &str) -> Player { Player { name: name.to_string(), score: 0 } }, a seta aponta para Player dentro da função, com missing `level` (falta level). Correção: acrescente level nessa struct.

Teste o que aprendeu

1. O rustc diz: missing fields a, b, c and 2 other fields. Quantos campos estão faltando?

2. A sua struct tem um campo Option<String> de que você ainda não precisa. O que você escreve nele?

3. Do que Config { port: 80, ..Default::default() } precisa para compilar?

Pontuação: 0 / 3