Erro E0451 do Rust: field of struct is private (campo privado ao criar)
O E0451 quer dizer: você tentou montar uma struct com um literal como Account { ... }, mas um dos campos dela é privado. Para escrever um literal de struct, você precisa preencher todos os campos, então basta um campo privado para você ficar de fora. Isso é de propósito: o módulo quer que você passe pelo construtor dele. A correção quase sempre é chamar (ou criar) uma função new. Para os outros erros desta série, veja Erros do compilador Rust.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0451]: field `balance` of struct `Account` is private --> src/main.rs:21:9 |19 | let account = bank::Account { | ------------- in this type20 | owner: String::from("Ferris"),21 | balance: 1_000_000, // ❌ campo privado | ^^^^^^^ private fieldEm português: “o campo balance da struct Account é privado”. O rustc marca o tipo que você está montando (in this type, neste tipo) e o campo que você não pode preencher (private field, campo privado).
Este é o programa que gera o erro:
mod bank { pub struct Account { pub owner: String, balance: i64, // privado }
impl Account { pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }
pub fn balance(&self) -> i64 { self.balance } }}
fn main() { let account = bank::Account { owner: String::from("Ferris"), balance: 1_000_000, // ❌ campo privado }; println!("{} has {}", account.owner, account.balance());}Você também pode ver um dos primos próximos dele:
| O que você escreveu | O que o rustc diz |
|---|---|
| o campo privado no literal | error[E0451]: field `balance` of struct `Account` is private |
Config { verbose: true, ..Default::default() } |
E0451 também, apontando para ..Default::default() (veja a Correção 3) |
| o literal sem o campo privado | cannot construct `Account` with struct literal syntax due to private fields (não é possível construir com a sintaxe de literal por causa dos campos privados), muitas vezes com help: you might have meant to use the `new` associated function |
Percent(150) numa tuple struct |
E0603: tuple struct constructor `Percent` is private |
Todos eles têm as mesmas correções.
O que significa
Seção intitulada “O que significa”A seta aponta para o campo que você não tem permissão de preencher: balance. owner é pub, então preenchê-lo não tem problema. Mas um literal de struct é tudo ou nada. Você não pode deixar balance de fora (o Rust não preenche campos que faltam), e também não pode preenchê-lo. Então, fora de mod bank, simplesmente não há como escrever Account { ... }.
Imagine abrir uma conta num banco de verdade. Você não escreve o seu próprio saldo inicial no formulário. Você preenche o seu nome, o banco cria a conta, e o banco decide que ela começa no zero. Account::new é esse formulário.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Os campos de structs são privados por padrão, assim como funções e módulos (veja Módulos). Se o código de fora pudesse montar uma Account com o saldo que quisesse, as regras do módulo não valeriam nada. Alguém poderia criar uma conta com um milhão, ou com menos cinquenta. Como só mod bank pode escrever o literal, toda Account do programa é criada por um código que conhece as regras.
Tem um segundo benefício. Se mais tarde você acrescentar um campo privado, como created_at ou history, o código de fora do módulo não quebra, porque ele nunca escreveu o literal. Ele chama new, e você atualiza new num lugar só.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Use (ou crie) um construtor pub fn new
Seção intitulada “Correção 1: Use (ou crie) um construtor pub fn new”Escolha esta na maioria das vezes. Procure primeiro uma função new (ou from_..., with_...); o rustc muitas vezes aponta para ela. Se a struct é sua e não existe nenhuma, escreva uma:
mod bank { pub struct Account { pub owner: String, balance: i64, // privado }
impl Account { // Toda conta começa em 0. O dinheiro só entra por deposit(). pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }
pub fn deposit(&mut self, amount: i64) { if amount > 0 { self.balance += amount; } }
pub fn balance(&self) -> i64 { self.balance } }}
fn main() { let mut account = bank::Account::new("Ferris"); account.deposit(250); println!("{} tem {}", account.owner, account.balance());}Saída:
Ferris tem 250Dentro de mod bank o literal é permitido, então new pode escrever Account { ... } à vontade. Não esqueça o pub no próprio new; sem ele, você recebe o E0624 (associated function `new` is private). Se algumas entradas devem ser recusadas, faça new retornar Option<Account> ou Result<Account, String>; a página do E0603 mostra um exemplo. Mais sobre construtores em Structs.
Correção 2: Deixe os campos pub quando os dados são simples
Seção intitulada “Correção 2: Deixe os campos pub quando os dados são simples”Escolha esta quando qualquer combinação de valores serve. Uma cor é só três números; não existe cor “errada” contra a qual se proteger. Aí um literal é a forma mais clara de montá-la:
mod theme { pub struct Color { pub r: u8, pub g: u8, pub b: u8, }}
fn main() { let ferris_orange = theme::Color { r: 247, g: 76, b: 0 }; println!("#{:02x}{:02x}{:02x}", ferris_orange.r, ferris_orange.g, ferris_orange.b);}Saída:
#f74c00Você precisa de pub em todos os campos. Se só o seu próprio crate deve montar a struct, pub(crate) nos campos também funciona.
Quando manter o campo privado: quando um valor pode estar errado (um saldo negativo, uma porcentagem acima de 100, um e-mail sem @), ou quando dois campos precisam combinar entre si (um total que precisa ser igual à soma de items). Nesses casos, um literal deixaria as pessoas pularem a verificação, e a Correção 1 é a escolha certa.
Correção 3: Comece do Default e depois altere os campos públicos
Seção intitulada “Correção 3: Comece do Default e depois altere os campos públicos”Escolha esta para structs de “configurações” com valores padrão sensatos, em que alguns campos são públicos e outros não. Primeiro, a armadilha. A sintaxe de atualização de struct ..Default::default() parece que deveria funcionar, mas não funciona:
mod settings { pub struct Config { pub verbose: bool, retries: u32, // privado: precisa ficar entre 1 e 10 }
impl Default for Config { fn default() -> Config { Config { verbose: false, retries: 3 } } }}
fn main() { let config = settings::Config { verbose: true, ..Default::default() }; println!("{}", config.verbose);}error[E0451]: field `retries` of struct `Config` is private --> src/main.rs:15:54 |15 | let config = settings::Config { verbose: true, ..Default::default() }; | ^^^^^^^^^^^^^^^^^^ field `retries` is privateO rustc aponta para ..Default::default() e diz que o campo retries é privado. ..Default::default() continua sendo um literal de struct: ele copia retries para a nova struct, e isso conta como preencher o campo. Em vez disso, crie primeiro o valor padrão e altere os campos públicos depois:
mod settings { pub struct Config { pub verbose: bool, retries: u32, // privado: precisa ficar entre 1 e 10 }
impl Default for Config { fn default() -> Config { Config { verbose: false, retries: 3 } } }
impl Config { pub fn retries(&self) -> u32 { self.retries } }}
fn main() { let mut config = settings::Config::default(); config.verbose = true; // campos públicos podem ser alterados depois println!("verbose: {}, retries: {}", config.verbose, config.retries());}Saída:
verbose: true, retries: 3Se todos os campos forem pub, ..Default::default() funciona sem problema. São só os campos privados que o bloqueiam.
Correção 4: Ofereça métodos no estilo builder
Seção intitulada “Correção 4: Ofereça métodos no estilo builder”Escolha esta quando há várias opções e algumas delas têm regras. Cada método recebe a struct, altera uma configuração (verificando-a) e devolve a struct, então as chamadas se encadeiam bem:
mod settings { pub struct Config { verbose: bool, retries: u32, }
impl Config { pub fn new() -> Config { Config { verbose: false, retries: 3 } }
pub fn verbose(mut self, on: bool) -> Config { self.verbose = on; self }
// A regra mora aqui: de 1 a 10 tentativas. pub fn retries(mut self, n: u32) -> Config { self.retries = n.clamp(1, 10); self }
pub fn describe(&self) -> String { format!("verbose: {}, retries: {}", self.verbose, self.retries) } }}
fn main() { let config = settings::Config::new().verbose(true).retries(50); println!("{}", config.describe());}Saída:
verbose: true, retries: 10Pedir 50 tentativas virou 10 sem alarde, porque a regra mora num lugar só. Muitos crates fazem isso, às vezes com um tipo ConfigBuilder separado e uma chamada final a .build(). Veja Padrões de projeto para essa versão.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”Depois de mover uma struct para um módulo. Todo User { name, email } em main.rs funcionava até User ir para mod user (ou src/user.rs). Agora todos falham. Decida struct por struct: dados simples ganham campos pub (Correção 2); qualquer coisa com regras ganha um pub fn new (Correção 1).
Tipos da biblioteca padrão ou de um crate. Você não pode escrever Vec { ... } nem Duration { ... }. O rustc diz cannot construct `Vec<_, _>` with struct literal syntax due to private fields e lista funções como Vec::new() e Vec::with_capacity(_). Use essas, ou Duration::from_secs(5).
Uma struct #[non_exhaustive] de outro crate. Aqui nem os campos públicos ajudam, e o código do erro é outro: error[E0639]: cannot create non-exhaustive struct using struct expression (não é possível criar uma struct não exaustiva com uma expressão de struct). O crate quer acrescentar campos mais tarde sem quebrar o seu código, então use o construtor dele ou o Default, exatamente como nas Correções 1 e 3.
Erros relacionados
Seção intitulada “Erros relacionados”- E0616: o mesmo campo privado, mas na hora de ler (
account.balance). - E0603:
tuple struct constructor `X` is private, e funções, structs ou módulos privados. - E0624:
associated function `new` is private. O seu construtor também precisa depub. - E0560:
struct `X` has no field named `y`. Um erro de digitação no nome de um campo do literal. - E0063 (em inglês):
missing field `x` in initializer of `Y`. Você deixou de fora um campo público. - Todos os erros do compilador: a lista completa de páginas de erro.
Teste o que aprendeu
Seção intitulada “Teste o que aprendeu”Teste o que aprendeu
1. pub struct Ticket { pub title: String, id: u32 } está em mod tickets. Como o main pode criar um Ticket?
2. Por que Config { verbose: true, ..Default::default() } falha quando Config tem um campo privado?
3. Qual struct combina bem com campos todos pub e literais simples?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0451 — Rust error codes index (em inglês): a explicação oficial, com um exemplo mínimo.
- Making Structs and Enums Public — The Rust Book (em inglês): uma struct com um campo privado que precisa de um construtor público.
- Struct visibility — Rust by Example (em inglês): montando structs com campos públicos e privados de fora de um módulo.
- Builder pattern — Rust Design Patterns (em inglês): o padrão builder completo, com um tipo builder separado.
Defaulttrait — Rust standard library (em inglês): como derivar e implementar valores padrão.