Pular para o conteúdo

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.

error[E0451]: field `balance` of struct `Account` is private
--> src/main.rs:21:9
|
19 | let account = bank::Account {
| ------------- in this type
20 | owner: String::from("Ferris"),
21 | balance: 1_000_000, // ❌ campo privado
| ^^^^^^^ private field

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

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.

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

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 250
▶ Experimente você mesmo

Dentro 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:

#f74c00
▶ Experimente você mesmo

Você 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 private

O 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: 3
▶ Experimente você mesmo

Se todos os campos forem pub, ..Default::default() funciona sem problema. São só os campos privados que o bloqueiam.

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: 10
▶ Experimente você mesmo

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

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.

  • 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 de pub.
  • 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

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