Zum Inhalt springen

Rust-Fehler E0616: field of struct is private (privates Feld)

E0616 bedeutet: Das Feld, nach dem du fragst, existiert – aber es ist privat in dem Modul, in dem das Struct definiert ist. Das Struct selbst darfst du benutzen, nur nicht hineingreifen. Der Fehler passiert jedem, der ein Struct zum ersten Mal in ein eigenes Modul verschiebt. Und jedem, der v.len statt v.len() tippt. Die Lösung ist meistens ein Methodenaufruf. Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.

error[E0616]: field `balance` of struct `Account` is private
--> src/main.rs:16:50
|
16 | println!("{} has {}", account.owner, account.balance); // ❌ privates Feld
| ^^^^^^^ private field

Auf Deutsch heißt das: „Das Feld balance des Structs Account ist privat.“ Dieses Programm erzeugt die Meldung:

mod bank {
pub struct Account {
pub owner: String,
balance: u64, // privat
}
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); // ❌ privates Feld
}

Denselben Fehler gibt es in mehreren Varianten:

Was du geschrieben hast Was rustc sagt Lösung
account.balance field `balance` of struct `Account` is private Lösung 2 oder 3
scores.len bei einem Vec field `len` of struct `Vec` is private, dazu help: a method `len` also exists, call it with parentheses Lösung 1
timeout.secs bei einer Duration field `secs` of struct `Duration` is private Lösung 1 (as_secs())
account.balance = 500 dasselbe E0616: Auch Schreiben ist verboten Lösung 2

Das help in der zweiten Zeile heißt: „Es gibt auch eine Methode len, ruf sie mit Klammern auf.“

Der Pfeil zeigt auf den Feldnamen nach dem Punkt. account.owner in derselben Zeile ist in Ordnung, weil owner mit pub markiert ist. balance ist es nicht. Deshalb darf nur Code innerhalb von mod bank das Feld lesen oder ändern.

Beachte, dass Account selbst pub ist. Genau das überrascht viele: Ein öffentliches Struct macht seine Felder nicht öffentlich. Jedes Feld bekommt sein eigenes pub – oder bleibt privat.

Stell dir eine Bank vor. Du darfst hineingehen, und jeder kann den Namen auf deinem Konto sehen. Aber du darfst nicht hinter den Schalter gehen und den Tresor öffnen, um das Geld zu zählen. Du fragst am Schalter nach, und dort hält man sich an die Regeln der Bank.

In Rust ist alles standardmäßig privat: Funktionen, Module und eben auch die Felder von Structs (siehe Module). Die Sichtbarkeit hängt am Modul, nicht am Typ. Jeder Code innerhalb von mod bank darf balance anfassen, Code von außerhalb nicht.

Der Sinn: Ein Struct kann seine eigenen Regeln schützen. Sagen wir, ein Kontostand darf nie negativ werden. Könnte jeder Teil des Programms account.balance = ... schreiben, könnte jeder Teil diese Regel brechen. Dann müsstest du die ganze Codebasis durchsuchen, um den Schuldigen zu finden. Ist das Feld privat, können es nur die paar Methoden in mod bank ändern. Prüfst du die Regel dort, gilt sie überall. Diese Idee heißt Kapselung (Encapsulation).

Die Standardbibliothek macht es genauso. Ein Vec hat private Felder für seine Länge und seinen Speicherpuffer. Könntest du bei einem Vektor mit 3 Elementen v.len = 100 schreiben, würde v[50] irgendeinen zufälligen Speicher lesen. Dass len privat bleibt, macht Vec erst sicher.

Lösung 1: Die Methode aufrufen, statt das Feld zu lesen

Abschnitt betitelt „Lösung 1: Die Methode aufrufen, statt das Feld zu lesen“

Nimm diese Lösung, wenn das Struct aus der Standardbibliothek oder einem anderen Crate kommt. Deren Code kannst du nicht ändern, und sie geben dir immer Methoden für alles, was du sehen darfst. Wenn rustc help: a method ... also exists sagt, ergänze die ():

use std::time::Duration;
fn main() {
let scores = vec![10, 20, 30];
println!("{} Ergebnisse", scores.len());
let timeout = Duration::from_secs(90);
println!("Timeout: {} Sekunden", timeout.as_secs());
}

Ausgabe:

3 Ergebnisse
Timeout: 90 Sekunden
▶ Selbst ausprobieren

Die Methode heißt nicht immer wie das Feld (secs liest du mit as_secs()). Schau auf der Seite des Typs bei docs.rs nach, oder lass dir von deinem Editor nach dem Punkt die Methoden anzeigen.

Lösung 2: Einen Getter ergänzen – und Methoden, die die Regeln einhalten

Abschnitt betitelt „Lösung 2: Einen Getter ergänzen – und Methoden, die die Regeln einhalten“

Nimm diese Lösung, wenn es dein eigenes Struct ist und das Feld eine Regel schützen soll. Lass das Feld privat und ergänze eine pub fn, die seinen Wert zurückgibt (einen Getter). Ändern darf man das Feld nur über Methoden, die die Regel prüfen:

mod bank {
pub struct Account {
pub owner: String,
balance: i64, // privat: nur dieses Modul darf es ändern
}
impl Account {
pub fn new(owner: &str) -> Account {
Account { owner: owner.to_string(), balance: 0 }
}
// Getter: Jeder darf den Kontostand lesen.
pub fn balance(&self) -> i64 {
self.balance
}
pub fn deposit(&mut self, amount: i64) {
if amount > 0 {
self.balance += amount;
}
}
// Hier wohnt die Regel: Der Kontostand fällt nie unter null.
pub fn withdraw(&mut self, amount: i64) -> Result<(), String> {
if amount > self.balance {
return Err(format!("nur {} verfügbar", self.balance));
}
self.balance -= amount;
Ok(())
}
}
}
fn main() {
let mut account = bank::Account::new("Ferris");
account.deposit(100);
println!("{} hat {}", account.owner, account.balance());
match account.withdraw(500) {
Ok(()) => println!("500 abgehoben"),
Err(e) => println!("500 abheben geht nicht: {e}"),
}
println!("Kontostand ist weiterhin {}", account.balance());
}

Ausgabe:

Ferris hat 100
500 abheben geht nicht: nur 100 verfügbar
Kontostand ist weiterhin 100
▶ Selbst ausprobieren

In Rust dürfen eine Methode und ein Feld denselben Namen haben. Deshalb heißt der Getter üblicherweise balance() (nicht get_balance()). Sobald es den Getter gibt, schlägt rustc ihn sogar selbst vor: help: a method `balance` also exists, call it with parentheses. deposit und withdraw sind deine Setter: Sie ändern das Feld, aber nur so, wie die Regeln es erlauben. Mehr über Methoden findest du unter Structs.

Lösung 3: Das Feld pub machen, wenn es einfache Daten sind

Abschnitt betitelt „Lösung 3: Das Feld pub machen, wenn es einfache Daten sind“

Nimm diese Lösung, wenn jeder Wert in Ordnung ist und nichts anderes von dem Feld abhängt. Ein Punkt auf einem Raster, eine Farbe oder ein Struct mit Einstellungen hat keine Regel zu schützen. Getter wären hier nur zusätzliche Tipparbeit:

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!("Punkt bei ({}, {})", p.x, p.y);
}

Ausgabe:

Punkt bei (4, 4)
▶ Selbst ausprobieren

Soll nur dein eigener Code das Feld sehen, schreib pub(crate) x: i32 (jedes Modul in diesem Crate) oder pub(super) x: i32 (nur das übergeordnete Modul). In einem Programm verhalten sie sich wie pub. In einer Library halten sie das Feld aus deiner öffentlichen API heraus. Auf der Seite zu E0603 findest du eine Tabelle mit allen Stufen.

Soll ich es einfach pub machen? Frag dich: „Kann ein bestimmter Wert dieses Felds – oder eine Änderung nur an diesem Feld – das Struct ungültig machen?“ Ein negativer Kontostand, ein len, das nicht zu den Daten passt, ein total, das nicht mehr der Summe der Posten entspricht: Solche Felder bleiben privat, und du nimmst Lösung 2. Lautet die Antwort „Nein, jeder Wert ist in Ordnung“, ist pub die ehrliche Wahl.

len bei einem Vec. Du hast scores.len geschrieben und field `len` of struct `Vec` is private bekommen. Das liegt daran, dass Vec tatsächlich ein privates Feld namens len hat. Die Lösung ist scores.len(). Lustiges Detail: name.len bei einem String liefert einen anderen Fehler, E0615 (attempted to take value of method, „Versuch, den Wert einer Methode zu nehmen“), weil String kein Feld mit diesem Namen hat. Die Lösung ist dieselbe.

Ein Feld von außen setzen. account.balance = 500; liefert dasselbe E0616. Auch Schreiben ist verboten. Mach das Feld nicht nur deshalb pub, um es setzen zu können. Ergänze lieber eine Methode wie deposit, die die Änderung vornimmt und prüft.

Nachdem du ein Struct in ein eigenes Modul oder eine eigene Datei verschoben hast. Der Code in main.rs hat monatelang user.email benutzt. Dann hast du User nach src/user.rs verschoben, und jeder Feldzugriff ist kaputt. Felder, die vorher in Ordnung waren, sind jetzt privat in mod user. Gib den Feldern mit einfachen Daten ein pub und den übrigen Getter.

Tests. Ein #[cfg(test)] mod tests-Block in derselben Datei ist ein Kind-Modul und darf deshalb private Felder lesen. Tests im Ordner tests/ liegen außerhalb deines Crates. Sie können, wie jeder andere Benutzer, nur die pub-API benutzen.

  • E0603: function `x` is private. Dieselbe Idee für Funktionen, Structs und Module.
  • E0451: dasselbe private Feld, aber beim Erzeugen des Structs mit Account { ... }.
  • E0624: method `x` is private. Auch die Methode (oder new) braucht pub.
  • E0609: no field `x` on type `T` . Das Feld gibt es überhaupt nicht (oft ein Tippfehler).
  • E0615: attempted to take value of method. Du hast x.len geschrieben und x.len() gemeint.
  • Alle Compilerfehler: die vollständige Liste der Fehlerseiten.

Kurzer Check

1. pub struct Account { balance: u64 } steht in mod bank. Darf main account.balance lesen?

2. Du bekommst `field len of struct Vec is private`. Wie behebst du das?

3. Ein Struct Score hat ein Feld points, das zwischen 0 und 100 bleiben muss. Was ist das beste Design?

Punkte: 0 / 3