Zum Inhalt springen

Rust-Fehler E0610: primitive type doesn't have fields (Feld an einer Zahl)

E0610 bedeutet: Du hast something.field geschrieben, aber something ist eine einfache Zahl (oder ein bool oder char), und die haben überhaupt keine Felder. Fast immer heißt das: Die Variable ist nicht das, was du denkst. Vielleicht hast du die Zahl schon aus einem Struct herausgeholt, eine Funktion hat eine Zahl statt eines Structs zurückgegeben, oder du hast ein .0 zu viel geschrieben. Die Meldung nennt dir den genauen Typ, den Rust sieht. Damit bist du nur noch einen Schritt von der Lösung entfernt. (Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.)

error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
--> src/main.rs:3:22
|
3 | println!("{}", x.value); // ❌ x ist eine Zahl, sie hat kein Feld `value`
| ^^^^^

Auf Deutsch: „{integer} ist ein primitiver Typ und hat deshalb keine Felder.“

Dieses Programm erzeugt den Fehler:

fn main() {
let x = 5;
println!("{}", x.value); // ❌ x ist eine Zahl, sie hat kein Feld `value`
}

Der Typ in Backticks ist das, was Rust für deine Variable hält. Hier ist es `{integer}`. Das heißt: „irgendeine ganze Zahl, ich habe noch nicht entschieden, welche Sorte“ (Rust nimmt später i32, wenn nichts anderes entscheidet). Du siehst auch `{float}` für Kommazahlen, oder einen konkreten Typ wie u32, usize, f64, bool oder char.

Die ^^^^^ zeigen auf den Namen nach dem Punkt. Rust sagt damit nicht „value ist falsch geschrieben“. Es sagt: „Eine Zahl kann gar kein Feld haben, egal, wie du es nennst.“

Die Frage lautet also: Warum ist diese Variable eine Zahl? Das ist, als würdest du nach „dem Preisschild an der 5“ fragen. Ein Produkt hat ein Preisschild. Die Zahl 5 ist einfach nur eine Zahl.

Primitive Typen wie i32, u32, f64, bool und char sind einzelne Werte, die fest in die Sprache eingebaut sind. Sie sind keine Structs, also gibt es in ihnen nichts, was du mit einem Punkt und einem Namen erreichen könntest. Siehe Datentypen.

Methoden haben sie aber schon: x.abs(), x.pow(2), price.round(), c.is_alphabetic(). Methoden rufst du mit () auf. Felder sind Daten, die in einem Struct gespeichert sind, und nur Structs, Tupel und Unions haben welche.

Weil Rust jedes .field beim Kompilieren prüft, kann diese Verwechslung nie zu einem Absturz oder einem stillen undefined zur Laufzeit werden. Du erfährst es sofort, in genau der richtigen Zeile.

Nimm diese Lösung, wenn du das Feld schon vorher aus dem Struct geholt hast. Nach let count = counter.value; ist count die Zahl, also fragt count.value nach einem Feld an einem u32:

error[E0610]: `u32` is a primitive type and therefore doesn't have fields
--> src/main.rs:8:26
|
8 | println!("{}", count.value);
| ^^^^^

Benutze entweder die Zahl so, wie sie ist, oder geh zurück zum Struct und lies das Feld dort:

struct Counter {
value: u32,
}
fn main() {
let mut counter = Counter { value: 0 };
counter.value += 3;
let count = counter.value; // count ist jetzt ein einfacher u32
println!("{}", count); // ✅ die Zahl selbst benutzen
println!("{}", counter.value); // ✅ oder das Feld aus dem Struct lesen
}

Ausgabe:

3
3
▶ Selbst ausprobieren

Du weißt nicht, welchen Typ eine Variable hat? Fahr in deinem Editor mit der Maus darüber (mit rust-analyzer, siehe Rust-Werkzeuge). Oder schreib kurz let _: () = count;: rustc antwortet mit expected `()`, found `usize` („erwartet (), gefunden usize“) und verrät dir so den echten Typ. Lösch die Zeile danach wieder.

Lösung 2: Das Struct zurückgeben oder speichern, nicht die Zahl

Abschnitt betitelt „Lösung 2: Das Struct zurückgeben oder speichern, nicht die Zahl“

Nimm diese Lösung, wenn der Wert aus einer Funktion oder einer Collection kommt und du ein Struct erwartet hast. Zwei häufige Fälle:

  • Eine Funktion wie fn find_order() -> u32 gibt die ID einer Bestellung zurück, und dann scheitert order.total, weil order ein u32 ist.
  • Eine HashMap<&str, u32> speichert nur Punktestände, und dann scheitert scores["ana"].score, weil jeder Wert ein u32 ist und kein Player.

Entscheide, was du wirklich willst. Brauchst du mehrere Daten, ändere den Rückgabetyp oder den Werttyp auf das Struct:

use std::collections::HashMap;
struct Order {
id: u32,
total: u32, // in Cent
}
// gibt die ganze Order zurück, nicht nur eine Zahl
fn new_order(id: u32, total: u32) -> Order {
Order { id, total }
}
struct Player {
score: u32,
level: u8,
}
fn main() {
let order = new_order(7, 1999);
println!("Bestellung {} kostet {} Cent", order.id, order.total);
// die Map speichert ganze Player, nicht nur ihre Punkte
let mut players: HashMap<&str, Player> = HashMap::new();
players.insert("ana", Player { score: 10, level: 2 });
let ana = &players["ana"];
println!("ana: Punkte {}, Level {}", ana.score, ana.level);
}

Ausgabe:

Bestellung 7 kostet 1999 Cent
ana: Punkte 10, Level 2
▶ Selbst ausprobieren

Brauchst du nur eine Zahl, behalte den u32 und lass einfach das .field weg. Siehe Structs und HashMaps.

Nimm diese Lösung, wenn der Fehler auf ein .0 oder .1 zeigt. Tupel und Tupel-Structs benutzen Positionen für ihre Felder, und da geht man leicht eine Ebene zu tief. Nach let x = point.0; ist x schon die Zahl, also ergibt x.0 die Meldung `{integer}` is a primitive type. Dasselbe passiert bei einer Hülle wie struct Meters(u32);: Ein .0 holt den u32 heraus, ein zweites .0 scheitert.

struct Meters(u32);
fn main() {
let point = (3, 4);
let x = point.0; // x ist schon die Zahl 3
println!("x = {}", x); // nicht x.0
let m = Meters(42);
println!("{} m", m.0); // ein .0 holt den u32 aus dem Tupel-Struct
let line = ((0, 0), (3, 4)); // verschachtelte Tupel: eine Position pro Ebene
println!("Ende y = {}", line.1.1);
}

Ausgabe:

x = 3
42 m
Ende y = 4
▶ Selbst ausprobieren

Zähl die Ebenen: Jedes .0 oder .1 geht eine Ebene tiefer in ein Tupel. Bist du bei einer Zahl angekommen, hör auf. Werden die Positionen unübersichtlich, gib ihnen mit einem Struct lieber Namen. Siehe Per Position auf Elemente zugreifen und Tupel oder Struct?.

Eine Methode ohne (). price.round an einem f64 oder x.abs an einem i32 ist nicht E0610, sondern E0615, „attempted to take value of method“ (Versuch, den Wert einer Methode zu nehmen), weil diese Namen wirklich Methoden sind. Lösung: price.round(), x.abs(). Eine Überraschung gibt es: Ist der Typ noch {integer} (wie bei let x = -5;), ergibt sogar x.abs den Fehler E0610, und x.abs() ergibt dann E0689 „can’t call method abs on ambiguous numeric type {integer}“ (Methode kann auf einem mehrdeutigen Zahlentyp nicht aufgerufen werden). Leg einen Typ fest, let x: i32 = -5;, und x.abs() funktioniert.

Eine Referenz auf eine Zahl. Ist die Variable ein &u32 statt eines u32, wechselt der Code zu E0609: no field `value` on type `&u32`. Typisch ist eine Schleife wie for (name, score) in &players, in der score ein &i32 ist und score.1 die Meldung no field `1` on type `&{integer}` ergibt. Gleiche Ursache, gleiche Lösungen: score ist schon die Zahl.

Das Ergebnis von len() oder count(). let len = scores.len(); gibt dir einen usize, also scheitert len.count mit `usize` is a primitive type. Lösung: Benutze len direkt.

Zahlen mit einer Einheit. Willst du price.cents oder distance.meters, bau ein kleines Struct: struct Price { cents: u64 }. Dann funktioniert price.cents, und der Typ verhindert, dass du Preise mit anderen Zahlen verwechselst. Siehe Structs.

Kurzer Check

1. Was ergibt total.amount nach let total = cart.total; (wobei total ein Feld vom Typ u32 ist)?

2. Was bedeutet {integer} in der Meldung?

3. let x = point.0; und danach x.0 ergibt E0610. Was ist die Lösung?

Punkte: 0 / 3