Zum Inhalt springen

Rust-Fehler E0609: no field X on type T (Feld nicht gefunden)

E0609 bedeutet: Du hast value.something geschrieben, aber der Typ von value hat kein Feld namens something. Meistens ist es ein Tippfehler, oder der Wert hat nicht ganz den Typ, den du denkst (zum Beispiel eine Option<User> statt eines User). Die Fehlermeldung nennt dir genau den Typ, den Rust sieht, und das reicht meistens schon für die Lösung. (Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.)

error[E0609]: no field `emial` on type `User`
--> src/main.rs:12:25
|
12 | println!("{}", user.emial); // ❌ Tippfehler: das Feld heißt `email`
| ^^^^^ unknown field
|
help: a field with a similar name exists
|
12 - println!("{}", user.emial); // ❌ Tippfehler: das Feld heißt `email`
12 + println!("{}", user.email); // ❌ Tippfehler: das Feld heißt `email`
|

„No field emial on type User“ heißt „kein Feld emial im Typ User“. Das help sagt: Es gibt ein Feld mit einem ähnlichen Namen, nämlich email.

Dieses Programm erzeugt den Fehler:

struct User {
name: String,
email: String,
}
fn main() {
let user = User {
name: String::from("Ferris"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.emial); // ❌ Tippfehler: das Feld heißt `email`
}

Die Überschrift hat zwei Teile, und beide sind wichtig:

  • no field `emial`: der Name nach dem Punkt, den Rust nicht gefunden hat.
  • on type `User`: der Typ, den user nach Meinung von Rust hat.

Prüf also zuerst den Namen und dann den Typ. Ist der Typ in der Meldung nicht das Struct, das du erwartet hast, zum Beispiel Option<User>, &Option<User>, Vec<User> oder &T, dann ist der Name in Ordnung. Das Problem ist dann, dass du das Struct erst herausholen musst.

Das ist, als würdest du in einem Laden nach „dem blauen im zweiten Regal“ fragen, aber im falschen Laden stehen. Die Frage ist gut, du bist nur am falschen Ort.

In Rust stehen die Felder eines Structs fest, sobald du das Struct schreibst. Der Compiler kennt die vollständige Liste und prüft jedes .field beim Kompilieren. Es gibt kein „zur Laufzeit nachschauen und undefined bekommen“ wie in JavaScript, und keinen AttributeError wie in Python: Ein falscher Feldname landet nie in deinem Programm. Siehe Structs.

Hüllentypen wie Option<T> und Result<T, E> sind eigene Typen: Dein Struct steckt in ihnen, aber sie haben nicht seine Felder. Rust greift nicht für dich hindurch, denn der Wert ist vielleicht gar nicht da. Du musst sagen, was passieren soll, wenn er fehlt. Siehe Option.

Generischer Code folgt derselben strengen Regel. In fn greet<T>(x: &T) kann T jeder Typ sein, auch i32 oder bool, die überhaupt keine Felder haben. Rust prüft eine generische Funktion einmal für jedes mögliche T. Deshalb darfst du nur benutzen, was deine Trait Bounds (Trait-Schranken) versprechen. Und Traits können nur Methoden versprechen, keine Felder. Siehe Generics und Traits.

Nimm diese Lösung, wenn der Typ in der Meldung das Struct ist, das du erwartet hast. Such nach der Zeile help: a field with a similar name exists. rustc weiß meistens genau, welches Feld du gemeint hast:

struct User {
name: String,
email: String,
}
fn main() {
let user = User {
name: String::from("Ferris"),
email: String::from("[email protected]"),
};
println!("{}", user.name);
println!("{}", user.email); // ✅ der richtige Feldname
}

Ausgabe:

▶ Selbst ausprobieren

Gibt es keinen Hinweis, spring zur Definition des struct (in den meisten Editoren mit Strg+Klick oder F12 auf den Typ) und kopiere den Namen von dort. Bei Feldnamen zählt die Groß- und Kleinschreibung: userName und user_name sind verschiedene Felder.

Nimm diese Lösung, wenn die Meldung sagt, dass der Typ Option<User>, Option<&User> oder &Option<User> ist. Das passiert ständig bei Funktionen, die etwas vielleicht nicht finden, und bei Methoden wie .first(), .last(), .get() und .find():

error[E0609]: no field `name` on type `Option<User>`
--> src/main.rs:11:25
|
11 | println!("{}", user.name);
| ^^^^ unknown field
|
help: one of the expressions' fields has a field of the same name
|
11 | println!("{}", user.unwrap().name);
| +++++++++

Das help schlägt vor, mit .unwrap() an das Feld im Inneren zu kommen. Dieser Vorschlag kompiliert zwar, aber das Programm stürzt ab, wenn der Wert None ist. Besser: Entscheide, was in beiden Fällen passieren soll, mit if let, match oder map plus einem Standardwert:

struct User {
name: String,
}
fn find_user(id: u32) -> Option<User> {
if id == 1 {
Some(User { name: String::from("Ferris") })
} else {
None
}
}
fn main() {
// if let: den Code nur ausführen, wenn es einen User gibt
if let Some(user) = find_user(1) {
println!("Gefunden: {}", user.name);
}
// match: beide Fälle behandeln
match find_user(2) {
Some(user) => println!("Gefunden: {}", user.name),
None => println!("Kein Benutzer mit der ID 2"),
}
// map + unwrap_or: ein Feld nehmen oder einen Standardwert benutzen
let users = vec![User { name: String::from("Ada") }];
let first_name = users.first().map(|u| u.name.as_str()).unwrap_or("niemand");
println!("Erster Benutzer: {first_name}");
}

Ausgabe:

Gefunden: Ferris
Kein Benutzer mit der ID 2
Erster Benutzer: Ada
▶ Selbst ausprobieren

Dieselben Muster funktionieren auch mit einer Referenz auf eine Option. Ist maybe_user eine &Option<User>, schreib if let Some(user) = maybe_user, und user ist dann ein &User. (Bei &Option<User> gibt rustc überhaupt keinen Hinweis, deshalb lohnt es sich, das zu merken.) In einer Funktion, die Option zurückgibt, klappt auch let user = find_user(1)?;. Siehe Eine Option mit match behandeln und if let.

Nimm diese Lösung, wenn der Typ ein Tupel-Struct ist, wie struct Point(i32, i32);. Seine Felder haben keine Namen, nur Positionen. p.x ergibt deshalb no field `x` on type `Point` (mit dem Hinweis p.0). Benutze die Position oder pack den Wert in benannte Variablen aus. Willst du Namen, mach daraus ein Struct mit benannten Feldern:

struct Point(i32, i32); // Tupel-Struct: die Felder heißen .0 und .1
struct Size {
width: u32, // benannte Felder: .width und .height benutzen
height: u32,
}
fn main() {
let p = Point(3, 4);
println!("x = {}, y = {}", p.0, p.1);
let Point(x, y) = p; // oder in benannte Variablen auspacken
println!("Abstand zum Quadrat = {}", x * x + y * y);
let screen = Size { width: 1920, height: 1080 };
println!("{}x{}", screen.width, screen.height);
}

Ausgabe:

x = 3, y = 4
Abstand zum Quadrat = 25
1920x1080
▶ Selbst ausprobieren

Normale Tupel funktionieren genauso. pair.2 bei einem Tupel mit zwei Elementen ergibt E0609 mit der Notiz available fields are: `0`, `1` (verfügbare Felder sind 0 und 1). Siehe Per Position auf Elemente zugreifen.

Lösung 4: Bei einem generischen T eine Trait-Methode statt eines Felds benutzen

Abschnitt betitelt „Lösung 4: Bei einem generischen T eine Trait-Methode statt eines Felds benutzen“

Nimm diese Lösung, wenn die Meldung on type `&T` (oder T) sagt, mit der Markierung type parameter 'T' declared here (Typparameter T hier deklariert). Code wie dieser schlägt fehl, selbst wenn jeder Typ, den du übergibst, ein Feld name hat:

fn greet<T>(animal: &T) {
println!("Hallo, {}!", animal.name); // ❌ no field `name` on type `&T`
}

Traits können keine Felder verlangen, aber sie können eine Getter-Methode verlangen. Definiere einen kleinen Trait, implementiere ihn für jeden Typ und gib ihn T als Bound mit:

trait Named {
fn name(&self) -> &str;
}
struct Dog {
name: String,
}
struct Robot {
model: String,
}
impl Named for Dog {
fn name(&self) -> &str {
&self.name
}
}
impl Named for Robot {
fn name(&self) -> &str {
&self.model // das Feld muss nicht einmal gleich heißen
}
}
fn greet<T: Named>(thing: &T) {
println!("Hallo, {}!", thing.name()); // ein Methodenaufruf, kein Feld
}
fn main() {
greet(&Dog { name: String::from("Rex") });
greet(&Robot { model: String::from("R2-D2") });
}

Ausgabe:

Hallo, Rex!
Hallo, R2-D2!
▶ Selbst ausprobieren

Arbeitet die Funktion immer nur mit einem Typ, brauchst du gar kein Generic: Schreib fn greet(animal: &Dog), und animal.name funktioniert. Siehe Traits als Funktionsparameter.

Eine Methode ohne (). Schreibst du word.len oder cart.total (wobei total eine Methode ist), ist das nah an E0609, aber rustc meldet einen anderen Code: E0615, „attempted to take value of method“ (Versuch, den Wert einer Methode zu nehmen), mit dem Hinweis „use parentheses to call the method“ (benutze Klammern, um die Methode aufzurufen). Bei einem Vec ergibt scores.len den Fehler E0616, „field len of struct Vec is private“ (das Feld ist privat), weil Vec wirklich ein verstecktes Feld mit diesem Namen hat. Für beide ist die Lösung gleich: word.len(), scores.len(), cart.total(). Und "hello".length ist ganz normales E0609 auf &str: In Rust heißt die Methode len(). Siehe Strings.

Ein Vec statt eines einzelnen Elements. dogs.name auf einem Vec<Dog> ergibt no field `name` on type `Vec<Dog>`. Eine Liste hat keinen name, jedes Element schon. Lösung: Nimm eines mit dogs[0].name, oder lauf in einer Schleife darüber: for dog in &dogs { println!("{}", dog.name); }. Siehe Vektoren.

Ein Result statt des Werts. load_config().port ergibt no field `port` on type `Result<Config, String>`, mit demselben .unwrap()-Hinweis. Lösung: let config = load_config()?; in einer Funktion, die Result zurückgibt, oder ein match. Siehe Der ?-Operator.

Ein Feld an einer Zahl. age.value an einem u32 hat wieder einen anderen Code: E0610, „u32 is a primitive type and therefore doesn’t have fields“ (u32 ist ein primitiver Typ und hat deshalb keine Felder). Zahlen, bool und char haben überhaupt keine Felder. Wahrscheinlich meintest du eine Variable mit einem Struct.

Kurzer Check

1. Der Fehler sagt, es gibt kein Feld name im Typ Option<User>. Was sagt dir das?

2. Wie liest du das erste Feld von struct Meters(f64); aus einem Wert m?

3. Wie kannst du in fn show<T>(x: &T) von jedem T einen Namen lesen?

Punkte: 0 / 3