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.)
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“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"), }; println!("{}", user.name); println!("{}", user.emial); // ❌ Tippfehler: das Feld heißt `email`}Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“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, denusernach 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.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“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.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Die Schreibweise korrigieren
Abschnitt betitelt „Lösung 1: Die Schreibweise korrigieren“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"), }; println!("{}", user.name); println!("{}", user.email); // ✅ der richtige Feldname}Ausgabe:
FerrisGibt 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.
Lösung 2: Das Struct erst aus der Option holen
Abschnitt betitelt „Lösung 2: Das Struct erst aus der Option holen“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: FerrisKein Benutzer mit der ID 2Erster Benutzer: AdaDieselben 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.
Lösung 3: .0 und .1 bei Tupel-Structs benutzen
Abschnitt betitelt „Lösung 3: .0 und .1 bei Tupel-Structs benutzen“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 = 4Abstand zum Quadrat = 251920x1080Normale 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!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.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“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.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0599: no method named x found: die Methoden-Version dieses Fehlers. Oft mit denselben Ursachen: ein Tippfehler oder eine
Option, die du erst auspacken musst. - E0425: cannot find value in this scope: Die Variable vor dem Punkt gibt es gar nicht, oder du hast
self.vor einem Feld vergessen. - E0308: mismatched types: das, was du oft als Nächstes siehst, wenn du eine
Optionan einer Stelle auspackst und an einer anderen nicht. E0615: attempted to take value of method: Du hastx.lenstattx.len()geschrieben.E0616: field is private: Das Feld gibt es, aber es ist nichtpub, wielenbei einemVec. Benutze die Methoden des Typs.E0560: struct has no field named x: derselbe Tippfehler, aber beim Erzeugen des Structs (User { emial: ... }).E0610: primitive type doesn't have fields:.somethingan einer Zahl, einembooloder einemchar.- Alle Compilerfehler einfach erklärt
Kurzer Check
Abschnitt betitelt „Kurzer Check“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
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0609 – Rust error codes index (auf Englisch): die offizielle Erklärung dieses Fehlers.
- Strukturen definieren und instanziieren – Das Rust-Buch: benannte Felder, Zugriff mit dem Punkt und die Update-Syntax für Structs.
- Mit Tupel-Strukturen verschiedene Typen erzeugen – Das Rust-Buch: Structs, deren Felder du über ihre Position erreichst, etwa mit
.0. - Die Aufzählung Option – Das Rust-Buch: warum
Option<T>undTverschiedene Typen sind und warum du dieOptionerst auspacken musst. - Gemeinsames Verhalten definieren mit Traits – Das Rust-Buch: Trait-Methoden und Trait Bounds, das richtige Werkzeug für generischen Code.