Errore Rust E0451: field of struct is private (letterale di struct)
E0451 significa: hai provato a costruire una struct con un letterale come Account { ... }, ma uno dei suoi campi è privato. Per scrivere un letterale di struct devi compilare tutti i campi, quindi basta un solo campo privato per lasciarti fuori. È voluto: il modulo vuole che tu passi dal suo costruttore. La soluzione è quasi sempre chiamare (o aggiungere) una funzione new. Questa pagina fa parte della guida agli errori del compilatore.
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0451]: field `balance` of struct `Account` is private --> src/main.rs:21:9 |19 | let account = bank::Account { | ------------- in this type20 | owner: String::from("Ferris"),21 | balance: 1_000_000, // ❌ private field | ^^^^^^^ private fieldIl compilatore ti dice: «il campo balance della struct Account è privato», e indica il punto del letterale in cui provi a impostarlo.
Ecco il programma che lo provoca:
mod bank { pub struct Account { pub owner: String, balance: i64, // private }
impl Account { pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }
pub fn balance(&self) -> i64 { self.balance } }}
fn main() { let account = bank::Account { owner: String::from("Ferris"), balance: 1_000_000, // ❌ private field }; println!("{} has {}", account.owner, account.balance());}Potresti vedere invece uno dei suoi parenti stretti:
| Cosa hai scritto | Cosa dice rustc |
|---|---|
| il campo privato nel letterale | error[E0451]: field `balance` of struct `Account` is private |
Config { verbose: true, ..Default::default() } |
anche E0451, che indica ..Default::default() (vedi la Soluzione 3) |
| il letterale senza il campo privato | cannot construct `Account` with struct literal syntax due to private fields, spesso con help: you might have meant to use the `new` associated function |
Percent(150) per una tuple struct |
E0603: tuple struct constructor `Percent` is private |
Si risolvono tutti allo stesso modo.
Cosa significa
Sezione intitolata “Cosa significa”La freccia indica il campo che non puoi impostare: balance. owner è pub, quindi impostarlo va bene. Ma un letterale di struct è tutto o niente. Non puoi lasciare fuori balance (Rust non compila i campi mancanti), e non puoi nemmeno impostarlo. Quindi fuori da mod bank non c’è proprio modo di scrivere Account { ... }.
Immagina di aprire un vero conto in banca. Non puoi scrivere tu il saldo iniziale sul modulo. Scrivi il tuo nome, la banca crea il conto, e la banca decide che parte da zero. Account::new è quel modulo.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”I campi delle struct sono privati per impostazione predefinita, proprio come funzioni e moduli (vedi Moduli). Se il codice esterno potesse costruire un Account con il saldo che vuole, le regole del modulo non varrebbero niente. Qualcuno potrebbe creare un conto con un milione, o con meno cinquanta. Siccome solo mod bank può scrivere il letterale, ogni Account del programma viene creato da codice che conosce le regole.
C’è un secondo vantaggio. Se più avanti aggiungi un campo privato, come created_at o history, il codice fuori dal modulo non si rompe, perché non ha mai scritto il letterale. Chiama new, e tu aggiorni new in un solo punto.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: usa (o aggiungi) un costruttore pub fn new
Sezione intitolata “Soluzione 1: usa (o aggiungi) un costruttore pub fn new”Scegli questa strada quasi sempre. Cerca prima una funzione new (o from_..., with_...). Spesso è rustc stesso a indicartela. Se la struct è tua e non ce n’è una, scrivila:
mod bank { pub struct Account { pub owner: String, balance: i64, // privato }
impl Account { // Ogni conto parte da 0. I soldi entrano solo con deposit(). pub fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }
pub fn deposit(&mut self, amount: i64) { if amount > 0 { self.balance += amount; } }
pub fn balance(&self) -> i64 { self.balance } }}
fn main() { let mut account = bank::Account::new("Ferris"); account.deposit(250); println!("{} ha {}", account.owner, account.balance());}Output:
Ferris ha 250Dentro mod bank il letterale è permesso, quindi new può scrivere Account { ... } liberamente. Non dimenticare il pub su new stesso; senza ottieni E0624 (associated function `new` is private). Se alcuni input vanno rifiutati, fai restituire a new un Option<Account> o un Result<Account, String>; la pagina di E0603 ne mostra uno. Trovi di più sui costruttori in Struct.
Soluzione 2: rendi i campi pub quando i dati sono semplici
Sezione intitolata “Soluzione 2: rendi i campi pub quando i dati sono semplici”Scegli questa strada quando qualsiasi combinazione di valori va bene. Un colore è solo tre numeri; non c’è un colore «sbagliato» da cui proteggersi. Allora un letterale è il modo più chiaro per costruirlo:
mod theme { pub struct Color { pub r: u8, pub g: u8, pub b: u8, }}
fn main() { let ferris_orange = theme::Color { r: 247, g: 76, b: 0 }; println!("#{:02x}{:02x}{:02x}", ferris_orange.r, ferris_orange.g, ferris_orange.b);}Output:
#f74c00Ti serve pub su ogni campo. Se deve costruirla solo il tuo crate, va bene anche pub(crate) sui campi.
Quando invece tenere il campo privato: quando un valore potrebbe essere sbagliato (un saldo negativo, una percentuale sopra 100, un’email senza @), o quando due campi devono essere coerenti tra loro (un total che deve essere uguale alla somma degli items). Lì un letterale permetterebbe di saltare il controllo, e la scelta giusta è la Soluzione 1.
Soluzione 3: parti da Default, poi cambia i campi pubblici
Sezione intitolata “Soluzione 3: parti da Default, poi cambia i campi pubblici”Scegli questa strada per le struct di «impostazioni» con valori predefiniti sensati, dove alcuni campi sono pubblici e altri no. Prima, la trappola. La sintassi di aggiornamento ..Default::default() sembra che debba funzionare, ma non funziona:
mod settings { pub struct Config { pub verbose: bool, retries: u32, // private: must stay between 1 and 10 }
impl Default for Config { fn default() -> Config { Config { verbose: false, retries: 3 } } }}
fn main() { let config = settings::Config { verbose: true, ..Default::default() }; println!("{}", config.verbose);}error[E0451]: field `retries` of struct `Config` is private --> src/main.rs:15:54 |15 | let config = settings::Config { verbose: true, ..Default::default() }; | ^^^^^^^^^^^^^^^^^^ field `retries` is private..Default::default() è comunque un letterale di struct: copia retries nella nuova struct, e questo conta come impostarlo. Crea invece prima il valore predefinito e cambia i campi pubblici dopo:
mod settings { pub struct Config { pub verbose: bool, retries: u32, // privato: deve restare tra 1 e 10 }
impl Default for Config { fn default() -> Config { Config { verbose: false, retries: 3 } } }
impl Config { pub fn retries(&self) -> u32 { self.retries } }}
fn main() { let mut config = settings::Config::default(); config.verbose = true; // i campi pubblici si possono cambiare dopo println!("verbose: {}, tentativi: {}", config.verbose, config.retries());}Output:
verbose: true, tentativi: 3Se tutti i campi sono pub, ..Default::default() funziona benissimo. Lo bloccano solo i campi privati.
Soluzione 4: offri metodi in stile builder
Sezione intitolata “Soluzione 4: offri metodi in stile builder”Scegli questa strada quando ci sono diverse opzioni e alcune hanno delle regole. Ogni metodo riceve la struct, cambia un’impostazione (controllandola) e restituisce la struct, così le chiamate si concatenano bene:
mod settings { pub struct Config { verbose: bool, retries: u32, }
impl Config { pub fn new() -> Config { Config { verbose: false, retries: 3 } }
pub fn verbose(mut self, on: bool) -> Config { self.verbose = on; self }
// La regola vive qui: da 1 a 10 tentativi. pub fn retries(mut self, n: u32) -> Config { self.retries = n.clamp(1, 10); self }
pub fn describe(&self) -> String { format!("verbose: {}, tentativi: {}", self.verbose, self.retries) } }}
fn main() { let config = settings::Config::new().verbose(true).retries(50); println!("{}", config.describe());}Output:
verbose: true, tentativi: 10La richiesta di 50 tentativi è diventata 10 senza fare rumore, perché la regola vive in un solo punto. Molti crate fanno così, a volte con un tipo ConfigBuilder separato e una chiamata finale .build(). Vedi Design pattern per quella versione.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Dopo aver spostato una struct in un modulo. Ogni User { name, email } in main.rs funzionava finché User non è finito in mod user (o in src/user.rs). Ora falliscono tutti. Decidi struct per struct: i dati semplici hanno campi pub (Soluzione 2); tutto ciò che ha delle regole ha una pub fn new (Soluzione 1).
Tipi della libreria standard o di un crate. Non puoi scrivere Vec { ... } o Duration { ... }. rustc dice cannot construct `Vec<_, _>` with struct literal syntax due to private fields ed elenca funzioni come Vec::new() e Vec::with_capacity(_). Usa quelle, oppure Duration::from_secs(5).
Una struct #[non_exhaustive] di un altro crate. Qui non aiutano nemmeno i campi pubblici, e il codice è diverso: error[E0639]: cannot create non-exhaustive struct using struct expression. Il crate vuole poter aggiungere campi in futuro senza romperti il codice, quindi usa il suo costruttore o Default, esattamente come nelle Soluzioni 1 e 3.
Errori correlati
Sezione intitolata “Errori correlati”- E0616: lo stesso campo privato, ma mentre lo leggi (
account.balance). - E0603:
tuple struct constructor `X` is private, e funzioni, struct o moduli privati. - E0624:
associated function `new` is private. Anche il tuo costruttore ha bisogno dipub. - E0560:
struct `X` has no field named `y`. Un errore di battitura nel nome di un campo di un letterale. - E0063 (in inglese):
missing field `x` in initializer of `Y`. Hai lasciato fuori un campo pubblico. - Tutti gli errori del compilatore: l’elenco completo delle pagine sugli errori.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. pub struct Ticket { pub title: String, id: u32 } è in mod tickets. Come può main creare un Ticket?
2. Perché Config { verbose: true, ..Default::default() } fallisce quando Config ha un campo privato?
3. Quale struct va bene con tutti i campi pub e semplici letterali?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0451 — Rust error codes index (in inglese): la spiegazione ufficiale con un esempio minimo.
- Making Structs and Enums Public — The Rust Book (in inglese): una struct con un campo privato che ha bisogno di un costruttore pubblico.
- Struct visibility — Rust by Example (in inglese): costruire struct con campi pubblici e privati da fuori di un modulo.
- Builder pattern — Rust Design Patterns (in inglese): il pattern builder completo, con un tipo builder separato.
- Trait
Default— libreria standard di Rust (in inglese): derivare e implementare valori predefiniti.