Salta ai contenuti

Errore Rust E0603: function, struct or module is private (elemento privato)

E0603 è una buona notizia travestita: Rust ha trovato la funzione, la struct o il modulo che hai chiesto. Solo che non può dartelo, perché l’elemento è privato. In un modulo Rust tutto è privato finché non dici il contrario, quindi quasi tutti incontrano questo errore la prima volta che dividono il codice in moduli. La soluzione di solito è una parola, pub. Questa pagina fa parte della guida agli errori del compilatore.

error[E0603]: function `apply_discount` is private
--> src/main.rs:8:26
|
8 | let total = billing::apply_discount(200); // ❌ private
| ^^^^^^^^^^^^^^ private function
|
note: the function `apply_discount` is defined here
--> src/main.rs:2:5
|
2 | fn apply_discount(price: u32) -> u32 { // no `pub`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Il compilatore dice: «la funzione apply_discount è privata», e la nota mostra dove è definita. Questo è il programma che lo produce:

mod billing {
fn apply_discount(price: u32) -> u32 { // no `pub`
price * 90 / 100
}
}
fn main() {
let total = billing::apply_discount(200); // ❌ private
println!("Total: {total}");
}

La prima riga nomina il tipo di elemento, quindi lo stesso errore si presenta in qualche variante:

Cosa dice la prima riga Cosa è andato storto di solito Soluzione
function `apply_discount` is private il fn non ha pub Soluzione 1 o 2
struct `Order` is private la struct non ha pub Soluzione 1
module `prices` is private un mod lungo il percorso non ha pub Soluzione 1 o 3
function import `price_of` is private hai raggiunto un elemento tramite un semplice use dentro un altro modulo Soluzione 3
tuple struct constructor `Percent` is private la struct è pub, ma i suoi campi no Soluzione 4

Leggi le due frecce. La prima (riga 8) è dove usi l’elemento. La freccia della note: (riga 2) è dove l’elemento è definito. Rust ti sta dicendo: «Vedo apply_discount laggiù, ma non è mai stato segnato come condiviso, quindi il codice fuori da billing non può chiamarlo».

Il file non conta. Se billing vive in src/billing.rs, ottieni lo stesso errore, e la note: indica semplicemente src/billing.rs:1:1.

Pensa a un palazzo di uffici. Ogni modulo è una stanza con una porta. Un elemento privato è un attrezzo che resta nella sua stanza: chi sta nella stanza (e nelle stanze più piccole al suo interno) può usarlo, ma nessuno dal corridoio. pub mette l’attrezzo su uno scaffale nel corridoio.

Rust rende tutto privato per default: funzioni, struct, campi delle struct, moduli e perfino gli import use. La condivisione la attivi tu con pub. La lezione Moduli lo mostra fin dall’inizio.

Il motivo è la sicurezza del te stesso del futuro. Tutto ciò che è pub diventa una promessa: altro codice potrebbe dipenderne, quindi cambiarlo potrebbe rompere quel codice. Gli elementi privati sei libero di cambiarli, rinominarli o cancellarli, e il compilatore garantisce che nessuno fuori ci faccia affidamento. Le piccole funzioni di supporto restano di supporto, e una struct può proteggere le proprie regole (come «un saldo non è mai negativo») perché nessun altro può toccarne direttamente i campi.

Un altro dettaglio: la visibilità viene controllata per ogni passo di un percorso. shop::prices::price_of funziona solo se prices è visibile a te e price_of è visibile a te. Una pub fn dentro un modulo privato resta comunque fuori portata.

Soluzione 1: Aggiungi pub all’elemento (e a ogni modulo lungo il percorso)

Sezione intitolata “Soluzione 1: Aggiungi pub all’elemento (e a ogni modulo lungo il percorso)”

Scegli questa quando l’elemento deve essere usato da fuori del suo modulo. Nella maggior parte dei casi è la soluzione giusta:

mod billing {
pub fn apply_discount(price: u32) -> u32 {
price * 90 / 100
}
}
fn main() {
let total = billing::apply_discount(200);
println!("Totale: {total}");
}

Output:

Totale: 180
▶ Provalo tu

Se l’errore dice module `prices` is private, il pub mancante è sul modulo: scrivi pub mod prices { ... }. rustc ti segnala questo caso anche con una seconda etichetta, function `price_of` is not publicly re-exported. Con un file separato, il pub va sulla riga di dichiarazione: pub mod prices;.

Per una struct, pub struct Order rende pubblico il tipo, ma ogni campo ha comunque bisogno del suo pub se il codice esterno deve leggerlo o impostarlo. Trovi di più nella Soluzione 4.

Soluzione 2: Usa pub(crate) o pub(super) per «solo il mio codice»

Sezione intitolata “Soluzione 2: Usa pub(crate) o pub(super) per «solo il mio codice»”

Scegli questa quando l’elemento va condiviso, ma non con tutto il mondo. pub ha alcuni livelli, dal più piccolo al più grande:

Scrivi Chi può usare l’elemento
niente (privato) il suo modulo, e i moduli al suo interno
pub(super) anche il modulo genitore
pub(crate) qualsiasi codice di questo crate
pub tutti, compresi altri crate che dipendono dal tuo

In un programma (un binario), pub(crate) e pub si comportano quasi allo stesso modo. La differenza conta in una libreria: pub(crate) permette ai tuoi moduli di condividere una funzione di supporto senza farla diventare parte dell’API pubblica della libreria.

mod shop {
// Qualsiasi codice di questo crate può chiamarla.
pub(crate) fn store_name() -> &'static str {
"Frutta di Ferris"
}
pub mod receipt {
// Solo il modulo genitore, `shop`, può chiamare questa funzione di supporto.
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!("Benvenuto da {}", shop::store_name());
shop::print_receipt(&[("mela", 3), ("pera", 5)]);
// shop::receipt::line("mela", 3); // ❌ E0603: `line` è solo per `shop`
}

Output:

Benvenuto da Frutta di Ferris
== Frutta di Ferris ==
mela: 3
pera: 5
▶ Provalo tu

Togli il // davanti all’ultima riga e ottieni function `line` is private, anche se c’è scritto pub(super). main non è il genitore di receipt, quindi la porta resta chiusa.

Scegli questa quando il modulo interno è un dettaglio di implementazione, ma un elemento al suo interno deve essere facile da raggiungere. Lascia privato il modulo e metti l’elemento su un percorso pubblico più corto:

mod shop {
mod prices {
// resta privato: come sono salvati i prezzi è affare di shop
pub fn price_of(item: &str) -> u32 {
if item == "mela" { 3 } else { 5 }
}
}
pub use prices::price_of; // ora raggiungibile come shop::price_of
}
fn main() {
println!("Le mele costano {}", shop::price_of("mela"));
println!("Le pere costano {}", shop::price_of("pera"));
}

Output:

Le mele costano 3
Le pere costano 5
▶ Provalo tu

Questa è anche la soluzione per function import `price_of` is private. Un semplice use prices::price_of; dentro shop è una scorciatoia solo per shop; come tutto il resto, anche un use è privato. pub use trasforma la scorciatoia in una porta pubblica. I crate grandi lo fanno di continuo, ed è per questo che puoi scrivere serde::Serialize senza sapere in quale modulo interno si trova.

Soluzione 4: Aggiungi un pub fn new invece di rendere pubblici i campi

Sezione intitolata “Soluzione 4: Aggiungi un pub fn new invece di rendere pubblici i campi”

Scegli questa quando l’errore riguarda i campi di una struct o il suo costruttore. Qui la struct è pub, ma il suo campo no:

mod billing {
pub struct Percent(u8);
}
fn main() {
let discount = billing::Percent(150); // ❌ private constructor
}
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); // ❌ private constructor
| ^^^^^^^ private tuple struct constructor
|
help: consider making the field publicly accessible
|
2 | pub struct Percent(pub u8);
| +++

rustc ti dice che un costruttore è privato se anche uno solo dei campi è privato, e suggerisce pub u8: per un semplice tipo di dati va bene. Ma guarda il valore: 150 per cento! Spesso il campo privato è proprio lo scopo. Il modulo vuole controllare il valore prima che un Percent esista. Dagli un costruttore pubblico che fa il controllo:

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 il 10% di sconto: {}", ten.apply(200));
println!("150% è permesso? {}", billing::Percent::new(150).is_some());
}

Output:

200 con il 10% di sconto: 180
150% è permesso? false
▶ Provalo tu

Ora ogni Percent dell’intero programma è garantito tra 0 e 100, e apply non può mai andare sotto zero. Ricorda che anche new ha bisogno di pub; un metodo privato dà un errore diverso, E0624 (associated function `new` is private). Vedi Struct per saperne di più sui costruttori.

Un pacchetto con sia main.rs sia lib.rs. Sono due crate separati, e main.rs usa la libreria dall’esterno. Se lib.rs dice mod inventory;, allora use shop::inventory::count_items; in main.rs fallisce:

error[E0603]: module `inventory` is private
--> src/main.rs:1:11
|
1 | use shop::inventory::count_items;
| ^^^^^^^^^ private module

Soluzione: scrivi pub mod inventory; in lib.rs e pub fn count_items nel modulo. Attenzione: qui pub(crate) non basta. Per la libreria, main.rs è un altro crate, quindi una pub(crate) fn dà comunque function `count_items` is private. Vedi Cargo e crate.

Campi privati di una struct. Hanno i loro codici di errore. Creare una struct con un campo privato dà E0451, e leggerne uno dà E0616:

error[E0451]: field `balance` of struct `Account` is private
error[E0616]: field `balance` of struct `Account` is private

Soluzione: la stessa scelta della Soluzione 4. Aggiungi pub al campo se chiunque può cambiarlo, oppure lascialo privato e aggiungi pub fn new(...) più un getter come pub fn balance(&self) -> u64.

Una funzione trovata nel codice sorgente di un crate. Leggi un crate su GitHub, trovi la funzione di supporto perfetta e ottieni E0603 quando la chiami. Se gli autori del crate non l’hanno resa pub, non puoi usarla, e potrebbero cambiarla in qualsiasi momento. Cerca un modo pubblico per fare la stessa cosa nella pagina del crate su docs.rs; lì compaiono solo gli elementi pub.

  • E0432: unresolved import. L’elemento non è stato proprio trovato. Con E0603 è stato trovato, ma è privato.
  • E0433: failed to resolve, un percorso nel tuo codice che non porta da nessuna parte.
  • E0451 ed E0616: un campo di una struct è privato (quando crei la struct o quando leggi il campo).
  • E0624: un metodo o una funzione associata (come new) è privato.
  • Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.

Verifica veloce

1. mod shop { mod prices { pub fn price_of() {} } } e main chiama shop::prices::price_of(). Quale errore ottieni?

2. Una funzione di supporto in src/lib.rs è segnata pub(crate). src/main.rs dello stesso pacchetto può chiamarla?

3. Una pub struct Email(String); tiene il suo campo privato apposta. Qual è il modo gentile per permettere ad altri moduli di crearne una?

Punteggio: 0 / 3