Zum Inhalt springen

Rust-Fehler E0308: mismatched types (Typen passen nicht)

Fehler E0308 bedeutet: Rust hat einen Wert von einem bestimmten Typ erwartet, du hast aber einen Wert von einem anderen Typ hingeschrieben. Das ist wahrscheinlich der Fehler, den du in Rust am häufigsten siehst. Zum Glück nennt dir die Meldung fast immer beide Typen, deshalb ist er meist schnell behoben. (Du suchst einen anderen Fehler? In der Übersicht der Compilerfehler findest du alle.)

error[E0308]: mismatched types
--> src/main.rs:6:29
|
6 | let user = User { name: "Ferris" };
| ^^^^^^^^ expected `String`, found `&str`
|
help: try using a conversion method
|
6 | let user = User { name: "Ferris".to_string() };
| ++++++++++++

Dieses Programm erzeugt den Fehler:

struct User {
name: String,
}
fn main() {
let user = User { name: "Ferris" };
println!("{}", user.name);
}

Jede E0308-Meldung hat dieselbe Form: expected A, found B – also „erwartet A, gefunden B“.

  • Expected ist der Typ, den Rust an dieser Stelle braucht. Er kommt von woanders her: aus einer Typannotation, aus dem Parameter- oder Rückgabetyp einer Funktion, aus einem Struct-Feld oder aus einem anderen Zweig eines if/match.
  • Found ist der Typ des Werts, den du dort tatsächlich geschrieben hast.

Im Beispiel ist das Feld name als String deklariert, also erwartet Rust einen String. Aber "Ferris" ist ein String-Literal, und das hat den Typ &str. Die beiden sind verwandt, aber nicht derselbe Typ.

Achte auch auf die help:-Zeilen. Bei E0308 schlägt dir der Compiler oft genau die richtige Lösung vor, wie oben .to_string().

Rust wandelt Typen nie heimlich um. In manchen Sprachen wird eine Zahl stillschweigend zu einem String, oder eine große Zahl wird unbemerkt abgeschnitten, damit sie in einen kleineren Typ passt. Solche versteckten Umwandlungen verursachen echte Bugs: verlorene Daten, falsche Ergebnisse, Abstürze weit weg vom eigentlichen Fehler.

Deshalb macht Rust jede Umwandlung im Code sichtbar. Das ist ein bisschen mehr Tipparbeit. Dafür siehst du später beim Lesen genau, wo ein Wert seinen Typ wechselt und ob dieser Schritt schiefgehen kann. Die Typen selbst findest du unter Datentypen.

Jede Lösung unten beginnt mit der passenden Form der Fehlermeldung. So kannst du direkt zu der springen, die zu deiner passt.

error[E0308]: mismatched types
--> src/main.rs:1:46
|
1 | fn total_price(price: u32, quantity: u32) -> u32 {
| ----------- ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | price * quantity;
| - help: remove this semicolon to return this value

Siehst du found (), dann schau dir die letzte Zeile der Funktion an. In Rust ist der letzte Ausdruck eines Blocks ohne Semikolon sein Rückgabewert. Mit ; wird daraus eine Anweisung, und die Funktion gibt stattdessen () zurück (den Typ für „nichts“). Der Compiler sagt es auch: Der Funktionsrumpf hat keinen Ausdruck am Ende, also gibt er () zurück. Lösch das Semikolon:

fn total_price(price: u32, quantity: u32) -> u32 {
price * quantity // kein Semikolon: dieser Wert wird zurückgegeben
}
fn main() {
println!("Gesamt: {}", total_price(250, 3));
}

Ausgabe:

Gesamt: 750
▶ Selbst ausprobieren

Dasselbe passiert in if-Blöcken und in match-Armen. Mehr dazu unter Funktionen.

String ist ein eigener, wachsender String. &str ist eine geliehene Ansicht auf einen Text. Die ganze Geschichte steht unter Strings. Die Umwandlung ist in beide Richtungen kurz:

  • &str zu String: .to_string() oder String::from(...).
  • String zu &str: mit & ausleihen (oder .as_str() aufrufen).

Der zweite Fall zeigt sich als expected &str, found String, meistens wenn du einen String an eine Funktion übergibst, die &str nimmt.

struct User {
name: String,
}
fn greet(name: &str) {
println!("Hallo, {name}!");
}
fn main() {
// &str -> String: .to_string() oder String::from()
let user = User { name: "Ferris".to_string() };
// String -> &str: mit & ausleihen (oder .as_str() aufrufen)
greet(&user.name);
greet(user.name.as_str());
let mut tags: Vec<String> = Vec::new();
tags.push(String::from("rust"));
println!("{tags:?}");
}

Ausgabe:

Hallo, Ferris!
Hallo, Ferris!
["rust"]
▶ Selbst ausprobieren
error[E0308]: mismatched types
--> src/main.rs:3:22
|
3 | let count: i32 = names.len();
| --- ^^^^^^^^^^^ expected `i32`, found `usize`
| |
| expected due to this

i32, i64, u32 und usize sind alles verschiedene Typen, und Rust mischt sie nicht. Längen und Indizes sind immer usize. Der Compiler zeigt dir mit „expected due to this“ auch, woher die Erwartung kommt: hier von der Annotation i32. Wähl eine Umwandlung:

  • .into() (oder i64::from(x)), wenn bei der Umwandlung nie Daten verloren gehen können, etwa von i32 nach i64.
  • i64::try_from(x) (oder .try_into()), wenn der Wert vielleicht nicht passt. Das gibt ein Result zurück, und du entscheidest, was im Fehlerfall passiert.
  • as ist der schnelle Cast. Er schlägt nie fehl, schneidet aber Werte, die nicht passen, stillschweigend ab. Nimm ihn nur, wenn du weißt, dass der Wert klein ist.
fn average(total: i64, count: i64) -> i64 {
total / count
}
fn main() {
let scores: Vec<i32> = vec![80, 95, 70];
let total: i32 = scores.iter().sum();
// i32 -> i64 passt immer, also reicht .into()
let total: i64 = total.into();
// usize -> i64 passt vielleicht nicht, also fragt try_from erst nach
let count = i64::try_from(scores.len()).unwrap();
println!("Durchschnitt: {}", average(total, count));
// `as` geht auch, schneidet aber Werte, die nicht passen, stillschweigend ab
let players = scores.len() as i32;
println!("{players} Spieler");
}

Ausgabe:

Durchschnitt: 81
3 Spieler
▶ Selbst ausprobieren

Oft ist es am einfachsten, stattdessen die Annotation zu ändern: let count: usize = names.len();. Die Seiten Typumwandlung und Konvertierungen gehen tiefer.

Lösung 4: In Some einpacken oder die Option auspacken

Abschnitt betitelt „Lösung 4: In Some einpacken oder die Option auspacken“
error[E0308]: mismatched types
--> src/main.rs:3:27
|
3 | let position: usize = names.iter().position(|n| *n == "Ben");
| ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `Option<usize>`
| |
| expected due to this

Option<usize> ist kein usize. Es heißt „vielleicht ein usize, vielleicht nichts“. Diesen Konflikt gibt es in beide Richtungen:

  • Expected T, found Option<T>: Du musst den Fall „nichts“ behandeln, mit match, if let oder unwrap_or(default).
  • Expected Option<T>, found T: Deine Funktion gibt eine Option zurück, also pack den Wert ein: Some(10) statt 10.
fn find_discount(code: &str) -> Option<u32> {
if code == "SAVE10" {
return Some(10); // T -> Option<T>: in Some einpacken
}
None
}
fn main() {
let names = vec!["Ana", "Ben", "Cy"];
// Option<T> -> T: den Fall „nichts gefunden“ behandeln ...
match names.iter().position(|n| *n == "Ben") {
Some(i) => println!("Ben steht an Position {i}"),
None => println!("Ben ist nicht da"),
}
// ... oder mit unwrap_or einen Standardwert wählen
let discount: u32 = find_discount("HELLO").unwrap_or(0);
println!("Rabatt: {discount} %");
}

Ausgabe:

Ben steht an Position 1
Rabatt: 0 %
▶ Selbst ausprobieren

Der Compiler schlägt dir vielleicht .expect("REASON") vor. Das funktioniert auch, lässt dein Programm aber abstürzen, wenn der Wert None ist. Nimm es also nur, wenn None wirklich nicht vorkommen kann. Siehe Option.

Lösung 5: Alle if/else- und match-Zweige auf denselben Typ bringen

Abschnitt betitelt „Lösung 5: Alle if/else- und match-Zweige auf denselben Typ bringen“
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:3:52
|
3 | let label = if stock > 0 { "in stock" } else { stock };
| ---------- ^^^^^ expected `&str`, found integer
| |
| expected because of this

Wenn if oder match einen Wert liefert, muss Rust dessen einen Typ kennen. Deshalb muss jeder Zweig denselben Typ liefern. Der erste Zweig legt die Erwartung fest, und der Fehler zeigt auf den Zweig, der nicht passt („if and else have incompatible types“ heißt „if und else haben unverträgliche Typen“). Bei match lautet die Meldung „match arms have incompatible types“. Bring den abweichenden Zweig auf Linie:

fn status_text(code: u16) -> String {
match code {
200 => "OK".to_string(),
404 => "Nicht gefunden".to_string(),
_ => format!("Unbekannter Code {code}"), // jeder Arm ist ein String
}
}
fn main() {
let stock = 0;
let label = if stock > 0 { "auf Lager" } else { "ausverkauft" }; // beide &str
println!("{label}");
println!("{}", status_text(404));
println!("{}", status_text(418));
}

Ausgabe:

ausverkauft
Nicht gefunden
Unbekannter Code 418
▶ Selbst ausprobieren

Hier brauchte ein Arm format!, und das erzeugt einen String. Deshalb wurden die anderen Arme auch zu String. Siehe if/else und match.

  • Benutzereingaben lesen. let age: u32 = input.trim(); ergibt „expected u32, found &str“. Text ist keine Zahl. Lösung: Parse ihn: let age: u32 = input.trim().parse().unwrap(); (oder behandle den Fehler, siehe Result).
  • Literale in einen Vec<String> schieben. tags.push("rust") ergibt „expected String, found &str“. Lösung: tags.push("rust".to_string()), oder mach daraus einen Vec<&str>, wenn die Strings alle Literale sind.
  • Rechnen mit gemischten Zahlentypen. total + n, wobei total ein i64 und n ein i32 ist, ergibt E0308 (und oft E0277 daneben). Lösung: Wandle zuerst eine Seite um: total + i64::from(n).

Kurzer Check

1. Eine Funktion hat -> i32, aber der Fehler sagt „expected i32, found ()“. Was ist die wahrscheinlichste Ursache?

2. Eine Funktion nimmt &str, und du hast let name = String::from("Ana");. Wie übergibst du den Wert?

3. Welche Umwandlung kann nie stillschweigend Daten verlieren?

Punkte: 0 / 3