Error E0603 de Rust: function, struct or module is private (elemento privado)
E0603 es una buena noticia disfrazada: Rust encontró la función, el struct o el módulo que pediste. Lo que pasa es que no puede dártelo, porque el elemento es privado. Todo lo que hay en un módulo de Rust es privado hasta que digas lo contrario, así que este error le aparece a casi todo el mundo la primera vez que divide su código en módulos. La solución suele ser una sola palabra: pub. 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[E0603]: function `apply_discount` is private --> src/main.rs:8:26 |8 | let total = billing::apply_discount(200); // ❌ privada | ^^^^^^^^^^^^^^ private function |note: the function `apply_discount` is defined here --> src/main.rs:2:5 |2 | fn apply_discount(price: u32) -> u32 { // sin `pub` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Este es el programa que lo produce:
mod billing { fn apply_discount(price: u32) -> u32 { // sin `pub` price * 90 / 100 }}
fn main() { let total = billing::apply_discount(200); // ❌ privada println!("Total: {total}");}La primera línea dice qué tipo de elemento es, así que el mismo error tiene varias variantes:
| Lo que dice la primera línea | Lo que suele haber fallado | Solución |
|---|---|---|
function `apply_discount` is private |
el fn no tiene pub |
Solución 1 o 2 |
struct `Order` is private |
el struct no tiene pub |
Solución 1 |
module `prices` is private |
un mod del camino no tiene pub |
Solución 1 o 3 |
function import `price_of` is private |
llegaste al elemento a través de un use normal dentro de otro módulo |
Solución 3 |
tuple struct constructor `Percent` is private |
el struct es pub, pero sus campos no |
Solución 4 |
Qué significa
Sección titulada «Qué significa»Lee las dos flechas. La primera (línea 8) es donde usas el elemento. La flecha de note: (línea 2) es donde está definido. Rust te está diciendo: “veo apply_discount ahí, pero nunca se marcó como compartida, así que el código de fuera de billing no puede llamarla”. Eso es lo que significa “is private”: es privada.
El archivo da igual. Si billing vive en src/billing.rs, obtienes el mismo error, y la note: simplemente apunta a src/billing.rs:1:1.
Piensa en un edificio de oficinas. Cada módulo es una sala con su puerta. Un elemento privado es una herramienta que se queda en su sala: la gente de dentro (y la de las salas más pequeñas que hay dentro de ella) puede usarla, pero nadie desde el pasillo. pub pone la herramienta en una estantería del pasillo.
Por qué Rust hace esto
Sección titulada «Por qué Rust hace esto»Rust hace que todo sea privado por defecto: funciones, structs, campos de structs, módulos e incluso las importaciones con use. Tú decides compartir algo con pub. La lección de Módulos lo muestra desde el principio.
El motivo es la seguridad de tu yo del futuro. Todo lo que es pub se convierte en una promesa: otro código puede depender de ello, así que cambiarlo puede romper ese código. Los elementos privados puedes cambiarlos, renombrarlos o borrarlos con libertad, y el compilador garantiza que nadie de fuera depende de ellos. Las pequeñas funciones auxiliares siguen siendo auxiliares, y un struct puede proteger sus propias reglas (como “el saldo nunca es negativo”) porque nadie más puede tocar sus campos directamente.
Un detalle más: la privacidad se comprueba en cada paso de una ruta. shop::prices::price_of solo funciona si prices es visible para ti y price_of es visible para ti. Un pub fn dentro de un módulo privado sigue fuera de tu alcance.
Cómo solucionarlo
Sección titulada «Cómo solucionarlo»Solución 1: Añade pub al elemento (y a cada módulo del camino)
Sección titulada «Solución 1: Añade pub al elemento (y a cada módulo del camino)»Elige esta cuando el elemento está pensado para usarse desde fuera de su módulo. Casi siempre es la solución correcta:
mod billing { pub fn apply_discount(price: u32) -> u32 { price * 90 / 100 }}
fn main() { let total = billing::apply_discount(200); println!("Total: {total}");}Salida:
Total: 180Si el error dice module `prices` is private, el pub que falta es el del módulo: escribe pub mod prices { ... }. rustc también te avisa de este caso con una segunda etiqueta, function `price_of` is not publicly re-exported (la función no se reexporta públicamente). Con un archivo separado, el pub va en la línea de la declaración: pub mod prices;.
En un struct, pub struct Order hace público el tipo, pero cada campo sigue necesitando su propio pub si el código de fuera tiene que leerlo o cambiarlo. Hay más sobre esto en la Solución 4.
Solución 2: Usa pub(crate) o pub(super) para “solo mi código”
Sección titulada «Solución 2: Usa pub(crate) o pub(super) para “solo mi código”»Elige esta cuando el elemento tiene que compartirse, pero no con todo el mundo. pub tiene varios niveles, de menor a mayor:
| Escribes | Quién puede usar el elemento |
|---|---|
| nada (privado) | su propio módulo y los módulos que hay dentro de él |
pub(super) |
también el módulo padre |
pub(crate) |
cualquier código de este crate |
pub |
todo el mundo, incluidos otros crates que dependan del tuyo |
En un programa (un binario), pub(crate) y pub se comportan casi igual. La diferencia importa en una biblioteca: pub(crate) permite que tus módulos compartan una función auxiliar sin que forme parte de la API pública de la biblioteca.
mod shop { // Cualquier código de este crate puede llamarla. pub(crate) fn store_name() -> &'static str { "Ferris Fruits" }
pub mod receipt { // Solo el módulo padre, `shop`, puede llamar a esta función auxiliar. pub(super) fn line(item: &str, price: u32) -> String { format!("{item}: {price}") } }
pub fn print_receipt(items: &[(&str, u32)]) { println!("== {} ==", store_name()); for (item, price) in items { println!("{}", receipt::line(item, *price)); } }}
fn main() { println!("Te damos la bienvenida a {}", shop::store_name()); shop::print_receipt(&[("manzana", 3), ("pera", 5)]); // shop::receipt::line("manzana", 3); // ❌ E0603: `line` es solo para `shop`}Salida:
Te damos la bienvenida a Ferris Fruits== Ferris Fruits ==manzana: 3pera: 5Quita las // de delante de la última línea y obtendrás function `line` is private, aunque diga pub(super). main no es el padre de receipt, así que la puerta sigue cerrada.
Solución 3: Reexporta con pub use
Sección titulada «Solución 3: Reexporta con pub use»Elige esta cuando el módulo interior es un detalle interno, pero un elemento de dentro debería ser fácil de alcanzar. Deja el módulo privado y pon el elemento en una ruta más corta y pública:
mod shop { mod prices { // sigue siendo privado: cómo se guardan los precios es cosa de shop pub fn price_of(item: &str) -> u32 { if item == "manzana" { 3 } else { 5 } } }
pub use prices::price_of; // ahora se llega a ella como shop::price_of}
fn main() { println!("Las manzanas cuestan {}", shop::price_of("manzana")); println!("Las peras cuestan {}", shop::price_of("pera"));}Salida:
Las manzanas cuestan 3Las peras cuestan 5Esta es también la solución para function import `price_of` is private (la importación de la función es privada). Un use prices::price_of; normal dentro de shop es un atajo solo para shop; como todo lo demás, un use es privado. pub use convierte el atajo en una puerta pública. Los crates grandes lo hacen todo el tiempo; por eso puedes escribir serde::Serialize sin saber en qué módulo interior vive.
Solución 4: Añade un pub fn new en lugar de hacer públicos los campos
Sección titulada «Solución 4: Añade un pub fn new en lugar de hacer públicos los campos»Elige esta cuando el error habla de los campos de un struct o de su constructor. Aquí el struct es pub, pero su campo no:
mod billing { pub struct Percent(u8);}
fn main() { let discount = billing::Percent(150); // ❌ constructor privado}error[E0603]: tuple struct constructor `Percent` is private --> src/main.rs:6:29 |2 | pub struct Percent(u8); | -- a constructor is private if any of the fields is private...6 | let discount = billing::Percent(150); // ❌ constructor privado | ^^^^^^^ private tuple struct constructor |help: consider making the field publicly accessible |2 | pub struct Percent(pub u8); | +++rustc explica que un constructor es privado si cualquiera de los campos es privado, y sugiere pub u8 (hacer público el campo). Para un tipo que solo guarda datos, eso está bien. Pero mira el valor: ¡150 por ciento! Muchas veces el campo privado es justo lo que se busca. El módulo quiere comprobar el valor antes de que exista un Percent. Dale un constructor público que haga esa comprobación:
mod billing { pub struct Percent(u8);
impl Percent { pub fn new(value: u8) -> Option<Percent> { if value <= 100 { Some(Percent(value)) } else { None } }
pub fn apply(&self, price: u32) -> u32 { price * (100 - self.0 as u32) / 100 } }}
fn main() { let ten = billing::Percent::new(10).unwrap(); println!("200 con un 10% de descuento: {}", ten.apply(200)); println!("¿Se permite un 150%? {}", billing::Percent::new(150).is_some());}Salida:
200 con un 10% de descuento: 180¿Se permite un 150%? falseAhora cada Percent de todo el programa está garantizado entre 0 y 100, y apply nunca puede dar un desbordamiento por debajo de cero (underflow). Recuerda que new también necesita pub; un método privado da otro error, E0624 (associated function `new` is private). En Structs tienes más sobre los constructores.
Dónde suele aparecer
Sección titulada «Dónde suele aparecer»Un paquete con main.rs y lib.rs a la vez. Son dos crates distintos, y main.rs usa la biblioteca desde fuera. Si lib.rs dice mod inventory;, entonces use shop::inventory::count_items; en main.rs falla:
error[E0603]: module `inventory` is private --> src/main.rs:1:11 |1 | use shop::inventory::count_items; | ^^^^^^^^^ private modulerustc te dice que el módulo inventory de la biblioteca es privado. Solución: escribe pub mod inventory; en lib.rs y pub fn count_items en el módulo. Cuidado: aquí pub(crate) no basta. Para la biblioteca, main.rs es otro crate, así que un pub(crate) fn sigue dando function `count_items` is private. Mira Cargo y crates.
Campos privados de un struct. Tienen sus propios códigos de error. Crear un struct con un campo privado da E0451, y leer uno da E0616:
error[E0451]: field `balance` of struct `Account` is privateerror[E0616]: field `balance` of struct `Account` is privateLos dos dicen lo mismo: el campo balance del struct Account es privado. Solución: la misma elección que en la Solución 4. Añade pub al campo si cualquiera puede cambiarlo, o déjalo privado y añade pub fn new(...) más un getter como pub fn balance(&self) -> u64.
Una función que encontraste en el código fuente de un crate. Lees un crate en GitHub, encuentras la función auxiliar perfecta y te sale E0603 al llamarla. Si los autores del crate no la hicieron pub, no puedes usarla, y pueden cambiarla en cualquier momento. Busca una forma pública de hacer lo mismo en la página del crate en docs.rs; allí solo aparecen los elementos pub.
Errores relacionados
Sección titulada «Errores relacionados»- E0432:
unresolved import. El elemento no se encontró en absoluto. Con E0603 sí se encontró, pero es privado. - E0433:
failed to resolve, una ruta de tu código que no lleva a ninguna parte. - E0451 y E0616: un campo de un struct es privado (al crear el struct o al leer el campo).
- E0624: un método o una función asociada (como
new) es privado. - 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. Tienes mod shop { mod prices { pub fn price_of() {} } } y main llama a shop::prices::price_of(). ¿Qué error obtienes?
2. Una función auxiliar de src/lib.rs está marcada como pub(crate). ¿Puede llamarla src/main.rs, del mismo paquete?
3. Un pub struct Email(String); deja su campo privado a propósito. ¿Cuál es la forma amable de dejar que otros módulos creen uno?
Puntuación: 0 / 3
Para leer más
Sección titulada «Para leer más»- E0603 — Rust error codes index (en inglés): la explicación oficial, con un ejemplo mínimo.
- Paths for Referring to an Item in the Module Tree — The Rust Book (en inglés): cómo exponer rutas con
pub, y cómo funcionapuben structs y enums. También existe una traducción al español de la comunidad del libro. - Bringing Paths into Scope with the
useKeyword — The Rust Book (en inglés): cómo reexportar nombres conpub use. - Visibility — Rust by Example (en inglés):
pub,pub(crate),pub(super)ypub(in path)en un solo ejemplo ejecutable. - Visibility and privacy — The Rust Reference (en inglés): las reglas exactas, para cuando quieras la imagen completa.