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.
El mensaje de error
Sección titulada «El mensaje de error»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”.
Qué significa
Sección titulada «Qué significa»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.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»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.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»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 puntuacionesTiempo de espera: 90 segundosEl 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 100No se pueden retirar 500: solo hay 100 disponiblesEl saldo sigue siendo 100Rust 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)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.
Dónde lo verás normalmente
Sección titulada «Dónde lo verás normalmente»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.
Errores relacionados
Sección titulada «Errores relacionados»- 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 (onew) también necesitapub. - 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. Escribistex.leny querías decirx.len(). - Todos los errores del compilador: la lista completa de páginas de errores.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»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
Para leer más
Sección titulada «Para leer más»- E0616 — Rust error codes index (en inglés): la explicación oficial, con un ejemplo mínimo.
- Making Structs and Enums Public — El libro de Rust (en inglés): por qué un
pub structsigue teniendo campos privados, con un ejemplo de un restaurante. También existe una traducción al español de la comunidad del libro. - Encapsulation that Hides Implementation Details — El libro de Rust (en inglés): un struct que mantiene su media correcta gracias a que sus campos son privados.
- Struct visibility — Rust by Example (en inglés): campos públicos y privados uno al lado del otro, en un solo ejemplo ejecutable.
- Structs have private fields (C-STRUCT-PRIVATE) — Rust API Guidelines (en inglés): por qué las bibliotecas mantienen los campos privados, para poder cambiarlos más adelante.