Erro E0616 do Rust: field of struct is private (campo privado)
O E0616 quer dizer: o campo que você pediu existe, mas é privado do módulo onde a struct foi definida. Você pode ter a struct nas mãos, só não pode mexer dentro dela. Acontece com todo mundo na primeira vez que coloca uma struct num módulo próprio, e com todo mundo que digita v.len em vez de v.len(). A correção costuma ser uma chamada de método. Para os outros erros desta série, veja Erros do compilador Rust.
A mensagem de erro
Seção intitulada “A mensagem de erro”error[E0616]: field `balance` of struct `Account` is private --> src/main.rs:16:50 |16 | println!("{} has {}", account.owner, account.balance); // ❌ campo privado | ^^^^^^^ private fieldEm português: “o campo balance da struct Account é privado”. A seta aponta para o campo, com o rótulo private field (campo privado).
Este é o programa que gera o erro:
mod bank { pub struct Account { pub owner: String, balance: u64, // privado }
impl Account { pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } } }}
fn main() { let account = bank::Account::new("Ferris"); println!("{} has {}", account.owner, account.balance); // ❌ campo privado}O mesmo erro aparece em algumas versões:
| O que você escreveu | O que o rustc diz | Correção |
|---|---|---|
account.balance |
field `balance` of struct `Account` is private |
Correção 2 ou 3 |
scores.len num Vec |
field `len` of struct `Vec` is private, mais help: a method `len` also exists, call it with parentheses (existe um método len, chame com parênteses) |
Correção 1 |
timeout.secs numa Duration |
field `secs` of struct `Duration` is private |
Correção 1 (as_secs()) |
account.balance = 500 |
o mesmo E0616: escrever também é bloqueado | Correção 2 |
O que significa
Seção intitulada “O que significa”A seta aponta para o nome do campo depois do ponto. account.owner, na mesma linha, não dá problema, porque owner está marcado com pub. balance não está, então só o código dentro de mod bank pode ler ou alterar esse campo.
Repare que a própria Account é pub. É isso que surpreende as pessoas: tornar uma struct pública não torna os campos dela públicos. Cada campo ganha o seu próprio pub, ou continua privado.
Pense num banco. Você pode entrar na agência, e qualquer um pode ver o nome na sua conta. Mas você não pode passar para trás do balcão e abrir o cofre para contar o dinheiro. Você pede ao caixa, e o caixa segue as regras do banco.
Por que o Rust faz isso
Seção intitulada “Por que o Rust faz isso”Em Rust, tudo é privado por padrão: funções, módulos e também os campos de structs (veja Módulos). A privacidade é uma questão de módulos, não de tipos. Qualquer código dentro de mod bank pode mexer em balance, e o código de fora não pode.
A ideia é deixar a struct cuidar das próprias regras. Digamos que um saldo bancário nunca pode ficar negativo. Se qualquer parte do programa pudesse escrever account.balance = ..., qualquer uma delas poderia quebrar essa regra, e você teria que vasculhar o código inteiro para descobrir qual foi. Se o campo é privado, só os poucos métodos dentro de mod bank podem alterá-lo. Verifique a regra ali, e ela vale em todo lugar. Essa ideia se chama encapsulamento.
A biblioteca padrão faz o mesmo. Um Vec tem campos privados para o tamanho e para o buffer de memória. Se você pudesse escrever v.len = 100 num vetor com 3 itens, ler v[50] leria memória aleatória. Manter len privado é o que torna o Vec seguro de usar.
Como corrigir
Seção intitulada “Como corrigir”Correção 1: Chame o método em vez de ler o campo
Seção intitulada “Correção 1: Chame o método em vez de ler o campo”Escolha esta quando a struct vem da biblioteca padrão ou de outro crate. Você não pode mudar o código deles, e eles sempre oferecem métodos para o que você tem permissão de ver. Quando o rustc disser help: a method ... also exists, acrescente os ():
use std::time::Duration;
fn main() { let scores = vec![10, 20, 30]; println!("{} pontuações", scores.len());
let timeout = Duration::from_secs(90); println!("Tempo limite: {} segundos", timeout.as_secs());}Saída:
3 pontuaçõesTempo limite: 90 segundosO nome do método nem sempre é o nome do campo (secs é lido com as_secs()). Consulte a página do tipo no docs.rs, ou deixe o seu editor listar os métodos depois que você digitar o ponto.
Correção 2: Crie um getter, e métodos que seguem as regras
Seção intitulada “Correção 2: Crie um getter, e métodos que seguem as regras”Escolha esta quando a struct é sua e o campo tem uma regra a proteger. Mantenha o campo privado, crie um pub fn que devolve o valor dele (um getter) e só altere o campo por meio de métodos que verificam a regra:
mod bank { pub struct Account { pub owner: String, balance: i64, // privado: só este módulo pode alterar }
impl Account { pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }
// Getter: qualquer um pode ler o saldo. pub fn balance(&self) -> i64 { self.balance }
pub fn deposit(&mut self, amount: i64) { if amount > 0 { self.balance += amount; } }
// A regra mora aqui: o saldo nunca fica abaixo de zero. pub fn withdraw(&mut self, amount: i64) -> Result<(), String> { if amount > self.balance { return Err(format!("só {} disponível", self.balance)); } self.balance -= amount; Ok(()) } }}
fn main() { let mut account = bank::Account::new("Ferris"); account.deposit(100); println!("{} tem {}", account.owner, account.balance());
match account.withdraw(500) { Ok(()) => println!("Saque de 500 feito"), Err(e) => println!("Não dá para sacar 500: {e}"), } println!("O saldo continua {}", account.balance());}Saída:
Ferris tem 100Não dá para sacar 500: só 100 disponívelO saldo continua 100O Rust deixa um método e um campo terem o mesmo nome, então balance() é o nome habitual para o getter (e não get_balance()). Depois que o getter existe, o rustc até sugere o getter: help: a method `balance` also exists, call it with parentheses. deposit e withdraw são os seus setters: eles alteram o campo, mas só das formas que as regras permitem. Veja Structs para saber mais sobre métodos.
Correção 3: Deixe o campo pub quando os dados são simples
Seção intitulada “Correção 3: Deixe o campo pub quando os dados são simples”Escolha esta quando qualquer valor serve e nada mais depende do campo. Um ponto numa grade, uma cor ou uma struct de configurações não têm regra nenhuma a proteger. Ali, getters são só digitação a mais:
mod geometry { pub struct Point { pub x: i32, pub y: i32, }}
fn main() { let mut p = geometry::Point { x: 3, y: 4 }; p.x += 1; println!("Ponto em ({}, {})", p.x, p.y);}Saída:
Ponto em (4, 4)Quando só o seu próprio código deve ver o campo, escreva pub(crate) x: i32 (qualquer módulo deste crate) ou pub(super) x: i32 (só o módulo pai). Num programa, eles se comportam como pub; numa biblioteca, deixam o campo fora da sua API pública. A página do E0603 tem uma tabela com todos os níveis.
Devo simplesmente deixar pub? Pergunte: “Algum valor desse campo, ou uma mudança só nele, poderia deixar a struct errada?” Um saldo negativo, um len que não bate com os dados, um total que não é mais a soma dos itens: mantenha esses privados e use a Correção 2. Se a resposta for “não, qualquer valor serve”, pub é a escolha honesta.
Onde você costuma ver esse erro
Seção intitulada “Onde você costuma ver esse erro”len num Vec. Você escreveu scores.len e recebeu field `len` of struct `Vec` is private. Isso acontece porque o Vec tem mesmo um campo privado chamado len. A correção é scores.len(). Detalhe curioso: name.len numa String dá outro erro, o E0615 (attempted to take value of method, tentativa de pegar o valor de um método), porque String não tem nenhum campo com esse nome. A correção é a mesma.
Alterar um campo de fora. account.balance = 500; dá o mesmo E0616. Escrever também é bloqueado. Não deixe o campo pub só para conseguir alterá-lo; crie um método como deposit, que faz a mudança e verifica.
Depois de mover uma struct para um módulo ou arquivo próprio. O código em main.rs usou user.email por meses. Aí você moveu User para src/user.rs, e todo acesso a campo quebrou. Campos que antes funcionavam agora são privados de mod user. Coloque pub nos campos que são dados simples, e getters no resto.
Testes. Um bloco #[cfg(test)] mod tests dentro do mesmo arquivo é um módulo filho, então ele pode ler campos privados. Testes na pasta tests/ ficam fora do seu crate e só podem usar a API pub, como qualquer outro usuário.
Erros relacionados
Seção intitulada “Erros relacionados”- E0603:
function `x` is private. A mesma ideia para funções, structs e módulos. - E0451: o mesmo campo privado, mas na hora de criar a struct com
Account { ... }. - E0624:
method `x` is private. O método (ou onew) também precisa depub. - E0609:
no field `x` on type `T`. O campo nem existe (muitas vezes é um erro de digitação). - E0615:
attempted to take value of method. Você escreveux.lene queriax.len(). - 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 Account { balance: u64 } está em mod bank. O main pode ler account.balance?
2. Você recebe `field len of struct Vec is private`. Qual é a correção?
3. Uma struct Score tem um campo points que precisa ficar entre 0 e 100. Qual é o melhor design?
Pontuação: 0 / 3
Para saber mais
Seção intitulada “Para saber mais”- E0616 — 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): por que uma
pub structainda tem campos privados, com um exemplo de restaurante. - Encapsulation that Hides Implementation Details — The Rust Book (em inglês): uma struct que mantém a média correta porque deixa os campos privados.
- Struct visibility — Rust by Example (em inglês): campos públicos e privados lado a lado, num único exemplo para rodar.
- Structs have private fields (C-STRUCT-PRIVATE) — Rust API Guidelines (em inglês): por que bibliotecas mantêm campos privados, para poder mudá-los depois.