Rust-Fehler E0451: field of struct is private (Struct-Literal)
E0451 bedeutet: Du wolltest ein Struct mit einem Literal wie Account { ... } bauen, aber eines seiner Felder ist privat. In einem Struct-Literal musst du jedes Feld ausfüllen. Ein einziges privates Feld reicht also, damit du draußen bleibst. Das ist Absicht: Das Modul will, dass du seinen eigenen Konstruktor benutzt. Die Lösung ist fast immer, eine new-Funktion aufzurufen (oder zu ergänzen). Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“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, // ❌ privates Feld | ^^^^^^^ private fieldAuf Deutsch heißt das: „Das Feld balance des Structs Account ist privat.“ Die Markierung in this type zeigt auf das Struct, das du gerade baust. Dieses Programm erzeugt die Meldung:
mod bank { pub struct Account { pub owner: String, balance: i64, // privat }
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, // ❌ privates Feld }; println!("{} has {}", account.owner, account.balance());}Vielleicht siehst du stattdessen einen seiner nahen Verwandten:
| Was du geschrieben hast | Was rustc sagt |
|---|---|
| das private Feld im Literal | error[E0451]: field `balance` of struct `Account` is private |
Config { verbose: true, ..Default::default() } |
ebenfalls E0451, mit dem Pfeil auf ..Default::default() (siehe Lösung 3) |
| das Literal ohne das private Feld | cannot construct `Account` with struct literal syntax due to private fields („wegen privater Felder nicht mit Literal-Syntax erzeugbar“), oft mit help: you might have meant to use the `new` associated function |
Percent(150) bei einem Tuple Struct |
E0603: tuple struct constructor `Percent` is private |
Alle lassen sich auf dieselbe Weise beheben.
Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Der Pfeil zeigt auf das Feld, das du nicht setzen darfst: balance. owner ist pub, das darfst du setzen. Aber ein Struct-Literal gilt ganz oder gar nicht. Du kannst balance nicht weglassen (Rust füllt fehlende Felder nicht selbst aus), und setzen darfst du es auch nicht. Außerhalb von mod bank gibt es also schlicht keine Möglichkeit, Account { ... } zu schreiben.
Stell dir vor, du eröffnest ein echtes Bankkonto. Deinen Startbetrag darfst du nicht selbst ins Formular schreiben. Du trägst deinen Namen ein, die Bank legt das Konto an, und die Bank entscheidet, dass es bei null beginnt. Account::new ist dieses Formular.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Die Felder eines Structs sind standardmäßig privat, genau wie Funktionen und Module (siehe Module). Könnte Code von außen ein Account mit beliebigem Kontostand bauen, wären die Regeln des Moduls wertlos. Jemand könnte ein Konto mit einer Million anlegen – oder mit minus fünfzig. Weil nur mod bank das Literal schreiben darf, wird jedes Account im Programm von Code erzeugt, der die Regeln kennt.
Es gibt noch einen zweiten Vorteil. Ergänzt du später ein privates Feld wie created_at oder history, bricht Code außerhalb des Moduls nicht, denn er hat das Literal nie geschrieben. Er ruft new auf, und du passt new an einer einzigen Stelle an.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Einen pub fn new-Konstruktor benutzen (oder ergänzen)
Abschnitt betitelt „Lösung 1: Einen pub fn new-Konstruktor benutzen (oder ergänzen)“Nimm diese Lösung in den meisten Fällen. Such zuerst nach einer Funktion new (oder from_..., with_...). rustc weist dich oft darauf hin. Ist es dein eigenes Struct und es gibt keine, schreib eine:
mod bank { pub struct Account { pub owner: String, balance: i64, // privat }
impl Account { // Jedes Konto beginnt bei 0. Geld kommt nur über deposit() hinein. 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!("{} hat {}", account.owner, account.balance());}Ausgabe:
Ferris hat 250Innerhalb von mod bank ist das Literal erlaubt, also darf new ganz normal Account { ... } schreiben. Vergiss das pub an new selbst nicht. Ohne bekommst du E0624 (associated function `new` is private). Sollen manche Eingaben abgelehnt werden, lass new ein Option<Account> oder Result<Account, String> zurückgeben. Die Seite zu E0603 zeigt ein Beispiel. Mehr über Konstruktoren findest du unter Structs.
Lösung 2: Die Felder pub machen, wenn es einfache Daten sind
Abschnitt betitelt „Lösung 2: Die Felder pub machen, wenn es einfache Daten sind“Nimm diese Lösung, wenn jede Kombination von Werten in Ordnung ist. Eine Farbe besteht nur aus drei Zahlen. Es gibt keine „falsche“ Farbe, vor der man sich schützen müsste. Dann ist ein Literal der klarste Weg, sie zu bauen:
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);}Ausgabe:
#f74c00Du brauchst pub an jedem Feld. Soll nur dein eigenes Crate das Struct bauen dürfen, funktioniert auch pub(crate) an den Feldern.
Wann das Feld besser privat bleibt: wenn ein Wert falsch sein kann (ein negativer Kontostand, ein Prozentwert über 100, eine E-Mail-Adresse ohne @) oder wenn zwei Felder zueinander passen müssen (ein total, das der Summe von items entsprechen muss). Dann könnte man mit einem Literal die Prüfung überspringen, und Lösung 1 ist die richtige Wahl.
Lösung 3: Mit Default starten und dann die öffentlichen Felder ändern
Abschnitt betitelt „Lösung 3: Mit Default starten und dann die öffentlichen Felder ändern“Nimm diese Lösung für Einstellungs-Structs mit sinnvollen Standardwerten, bei denen manche Felder öffentlich sind und manche nicht. Zuerst die Falle: Die Struct-Update-Syntax ..Default::default() sieht aus, als müsste sie funktionieren. Tut sie aber nicht:
mod settings { pub struct Config { pub verbose: bool, retries: u32, // privat: muss zwischen 1 und 10 bleiben }
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() ist trotzdem ein Struct-Literal: Es kopiert retries in das neue Struct, und das zählt als Setzen. Erzeuge stattdessen zuerst den Standardwert und ändere danach die öffentlichen Felder:
mod settings { pub struct Config { pub verbose: bool, retries: u32, // privat: muss zwischen 1 und 10 bleiben }
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; // öffentliche Felder darf man danach ändern println!("verbose: {}, retries: {}", config.verbose, config.retries());}Ausgabe:
verbose: true, retries: 3Ist jedes Feld pub, funktioniert ..Default::default() problemlos. Nur private Felder blockieren es.
Lösung 4: Methoden im Builder-Stil anbieten
Abschnitt betitelt „Lösung 4: Methoden im Builder-Stil anbieten“Nimm diese Lösung, wenn es mehrere Optionen gibt und für manche davon Regeln gelten. Jede Methode nimmt das Struct, ändert eine Einstellung (und prüft sie) und gibt das Struct zurück. So lassen sich die Aufrufe schön verketten:
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 }
// Hier wohnt die Regel: 1 bis 10 Versuche. pub fn retries(mut self, n: u32) -> Config { self.retries = n.clamp(1, 10); self }
pub fn describe(&self) -> String { format!("verbose: {}, retries: {}", self.verbose, self.retries) } }}
fn main() { let config = settings::Config::new().verbose(true).retries(50); println!("{}", config.describe());}Ausgabe:
verbose: true, retries: 10Aus den gewünschten 50 Versuchen wurden stillschweigend 10, weil die Regel an genau einer Stelle wohnt. Viele Crates machen das so, manchmal mit einem eigenen Typ ConfigBuilder und einem abschließenden Aufruf von .build(). Diese Variante zeigt die Seite Entwurfsmuster.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Nachdem du ein Struct in ein Modul verschoben hast. Jedes User { name, email } in main.rs hat funktioniert, bis User nach mod user (oder src/user.rs) umgezogen ist. Jetzt schlagen alle fehl. Entscheide für jedes Struct einzeln: Einfache Daten bekommen pub-Felder (Lösung 2). Alles mit Regeln bekommt ein pub fn new (Lösung 1).
Typen aus der Standardbibliothek oder einem Crate. Vec { ... } oder Duration { ... } kannst du nicht schreiben. rustc sagt cannot construct `Vec<_, _>` with struct literal syntax due to private fields und listet Funktionen wie Vec::new() und Vec::with_capacity(_) auf. Nimm diese, oder Duration::from_secs(5).
Ein #[non_exhaustive]-Struct aus einem anderen Crate. Hier helfen nicht einmal öffentliche Felder, und der Fehlercode ist ein anderer: error[E0639]: cannot create non-exhaustive struct using struct expression („ein nicht erschöpfendes Struct kann nicht per Struct-Ausdruck erzeugt werden“). Das Crate will später Felder ergänzen können, ohne deinen Code zu brechen. Nimm also seinen Konstruktor oder Default, genau wie in Lösung 1 und Lösung 3.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0616: dasselbe private Feld, aber beim Lesen (
account.balance). - E0603:
tuple struct constructor `X` is privatesowie private Funktionen, Structs oder Module. - E0624:
associated function `new` is private. Auch dein Konstruktor brauchtpub. - E0560:
struct `X` has no field named `y`. Ein Tippfehler im Feldnamen eines Literals. - E0063 (auf Englisch):
missing field `x` in initializer of `Y`. Du hast ein öffentliches Feld weggelassen. - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. pub struct Ticket { pub title: String, id: u32 } steht in mod tickets. Wie kann main ein Ticket erzeugen?
2. Warum schlägt Config { verbose: true, ..Default::default() } fehl, wenn Config ein privates Feld hat?
3. Welches Struct eignet sich gut für lauter pub-Felder und einfache Literale?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0451 – Rust error codes index (auf Englisch): die offizielle Erklärung mit einem minimalen Beispiel.
- Strukturen und Aufzählungen öffentlich machen – Das Rust-Buch: ein Struct mit privatem Feld, das einen öffentlichen Konstruktor braucht.
- Struct visibility – Rust by Example (auf Englisch): Structs mit öffentlichen und privaten Feldern von außerhalb eines Moduls bauen.
- Builder pattern – Rust Design Patterns (auf Englisch): das vollständige Builder-Muster mit einem eigenen Builder-Typ.
Default– Doku der Standardbibliothek (auf Englisch): Standardwerte ableiten und selbst implementieren.