Ir al contenido

Error E0616 de Rust: field of struct is private (campo privado)

E0616 significa: el campo que pediste existe, pero es privado del módulo donde está definido el struct. Puedes tener el struct, pero no meter la mano dentro. Le pasa a todo el mundo la primera vez que mueve un struct a su propio módulo, y también a quien escribe v.len en lugar de v.len(). La solución suele ser llamar a un método. Esta página forma parte de la guía de errores del compilador.

error[E0616]: field `balance` of struct `Account` is private
--> src/main.rs:16:50
|
16 | println!("{} has {}", account.owner, account.balance); // ❌ campo privado
| ^^^^^^^ private field

“Field balance of struct Account is private” quiere decir “el campo balance del struct Account es privado”. La flecha marca el campo privado (private field).

Este es el programa que lo produce:

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
}

El mismo error aparece con varias formas:

Lo que escribiste Lo que dice rustc Solución
account.balance field `balance` of struct `Account` is private Solución 2 o Solución 3
scores.len en un Vec field `len` of struct `Vec` is private, más help: a method `len` also exists, call it with parentheses Solución 1
timeout.secs en un Duration field `secs` of struct `Duration` is private Solución 1 (as_secs())
account.balance = 500 el mismo E0616: escribir también está bloqueado Solución 2

El help del Vec significa “también existe un método len, llámalo con paréntesis”.

La flecha apunta al nombre del campo que va después del punto. account.owner, en la misma línea, funciona bien, porque owner está marcado como pub. balance no lo está, así que solo el código de dentro de mod bank puede leerlo o cambiarlo.

Fíjate en que Account sí es pub. Eso es lo que sorprende a la gente: hacer público un struct no hace públicos sus campos. Cada campo lleva su propio pub, o se queda privado.

Piensa en un banco. Puedes entrar, y cualquiera puede ver el nombre de tu cuenta. Pero no puedes pasar detrás del mostrador, abrir la caja fuerte y contar el dinero. Se lo pides al cajero, y el cajero sigue las reglas del banco.

En Rust, todo es privado por defecto: las funciones, los módulos y también los campos de los structs (mira Módulos). La privacidad depende de los módulos, no de los tipos. Cualquier código dentro de mod bank puede tocar balance, y el código de fuera no.

La idea es que un struct pueda proteger sus propias reglas. Imagina que el saldo de una cuenta nunca puede ser negativo. Si cualquier parte del programa pudiera escribir account.balance = ..., cualquiera de ellas podría romper esa regla, y tendrías que buscar en todo el código cuál fue. Si el campo es privado, solo los pocos métodos de dentro de mod bank pueden cambiarlo. Compruebas la regla ahí, y se cumple en todas partes. Esta idea se llama encapsulación.

La biblioteca estándar hace lo mismo. Un Vec tiene campos privados para su longitud y su zona de memoria. Si pudieras escribir v.len = 100 en un vector con 3 elementos, leer v[50] leería memoria al azar. Mantener len privado es lo que hace que Vec sea seguro de usar.

Solución 1: Llama al método en lugar de leer el campo

Sección titulada «Solución 1: Llama al método en lugar de leer el campo»

Elige esta cuando el struct viene de la biblioteca estándar o de otro crate. No puedes cambiar su código, y siempre te dan métodos para lo que sí puedes ver. Cuando rustc dice help: a method ... also exists, añade los ():

use std::time::Duration;
fn main() {
let scores = vec![10, 20, 30];
println!("{} puntuaciones", scores.len());
let timeout = Duration::from_secs(90);
println!("Tiempo de espera: {} segundos", timeout.as_secs());
}

Salida:

3 puntuaciones
Tiempo de espera: 90 segundos
▶ Pruébalo tú mismo

El nombre del método no siempre es el del campo (secs se lee con as_secs()). Mira la página del tipo en docs.rs, o deja que tu editor te muestre la lista de métodos después de escribir el punto.

Solución 2: Añade un getter, y métodos que respeten las reglas

Sección titulada «Solución 2: Añade un getter, y métodos que respeten las reglas»

Elige esta cuando el struct es tuyo y el campo tiene una regla que proteger. Deja el campo privado, añade un pub fn que devuelva su valor (un getter) y cámbialo solo con métodos que comprueben la regla:

mod bank {
pub struct Account {
pub owner: String,
balance: i64, // privado: solo este módulo puede cambiarlo
}
impl Account {
pub fn new(owner: &str) -> Account {
Account { owner: owner.to_string(), balance: 0 }
}
// Getter: cualquiera puede leer el saldo.
pub fn balance(&self) -> i64 {
self.balance
}
pub fn deposit(&mut self, amount: i64) {
if amount > 0 {
self.balance += amount;
}
}
// Aquí vive la regla: el saldo nunca baja de cero.
pub fn withdraw(&mut self, amount: i64) -> Result<(), String> {
if amount > self.balance {
return Err(format!("solo hay {} disponibles", self.balance));
}
self.balance -= amount;
Ok(())
}
}
}
fn main() {
let mut account = bank::Account::new("Ferris");
account.deposit(100);
println!("{} tiene {}", account.owner, account.balance());
match account.withdraw(500) {
Ok(()) => println!("Retirados 500"),
Err(e) => println!("No se pueden retirar 500: {e}"),
}
println!("El saldo sigue siendo {}", account.balance());
}

Salida:

Ferris tiene 100
No se pueden retirar 500: solo hay 100 disponibles
El saldo sigue siendo 100
▶ Pruébalo tú mismo

Rust deja que un método y un campo tengan el mismo nombre, así que lo habitual es llamar al getter balance() (no get_balance()). En cuanto existe el getter, rustc incluso te lo sugiere: help: a method `balance` also exists, call it with parentheses. deposit y withdraw son tus setters: cambian el campo, pero solo de las formas que permiten las reglas. Mira Structs para saber más sobre los métodos.

Solución 3: Haz el campo pub cuando los datos son simples

Sección titulada «Solución 3: Haz el campo pub cuando los datos son simples»

Elige esta cuando cualquier valor sirve y nada más depende del campo. Un punto en una cuadrícula, un color o un struct de ajustes no tienen ninguna regla que proteger. Ahí los getters solo son escribir de más:

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!("Punto en ({}, {})", p.x, p.y);
}

Salida:

Punto en (4, 4)
▶ Pruébalo tú mismo

Cuando solo tu propio código debe ver el campo, escribe pub(crate) x: i32 (cualquier módulo de este crate) o pub(super) x: i32 (solo el módulo padre). En un programa se comportan como pub; en una biblioteca mantienen el campo fuera de tu API pública. La página de E0603 tiene una tabla con todos los niveles.

¿Y si simplemente lo hago pub? Pregúntate: “¿Podría algún valor de este campo, o un cambio solo en él, dejar el struct en un estado incorrecto?”. Un saldo negativo, un len que no coincide con los datos, un total que ya no es la suma de los elementos: esos, déjalos privados y usa la Solución 2. Si la respuesta es “no, cualquier valor sirve”, pub es la opción honesta.

len en un Vec. Escribiste scores.len y obtuviste field `len` of struct `Vec` is private. Es porque Vec de verdad tiene un campo privado llamado len. La solución es scores.len(). Detalle curioso: name.len en un String da otro error, E0615 (attempted to take value of method, intentaste tomar el valor de un método), porque String no tiene ningún campo con ese nombre. La solución es la misma.

Asignar un campo desde fuera. account.balance = 500; da el mismo E0616. Escribir también está bloqueado. No hagas el campo pub solo para asignarlo; añade un método como deposit que haga el cambio y lo compruebe.

Después de mover un struct a su propio módulo o archivo. El código de main.rs usó user.email durante meses. Luego moviste User a src/user.rs y se rompieron todos los accesos a campos. Los campos que antes funcionaban ahora son privados de mod user. Añade pub a los campos que son datos simples, y getters para el resto.

Pruebas. Un bloque #[cfg(test)] mod tests dentro del mismo archivo es un módulo hijo, así que puede leer campos privados. Las pruebas de la carpeta tests/ están fuera de tu crate y solo pueden usar la API pub, como cualquier otro usuario.

  • E0603: function `x` is private. La misma idea, pero para funciones, structs y módulos.
  • E0451: el mismo campo privado, pero al crear el struct con Account { ... }.
  • E0624: method `x` is private. El método (o new) también necesita pub.
  • E0609: no field `x` on type `T` . El campo no existe en absoluto (a menudo es una errata).
  • E0615: attempted to take value of method. Escribiste x.len y querías decir x.len().
  • Todos los errores del compilador: la lista completa de páginas de errores.

Comprueba lo aprendido

1. pub struct Account { balance: u64 } está en mod bank. ¿Puede main leer account.balance?

2. Te sale `field len of struct Vec is private`. ¿Cuál es la solución?

3. Un struct Score tiene un campo points que debe estar siempre entre 0 y 100. ¿Cuál es el mejor diseño?

Puntuación: 0 / 3