Rust-Fehler E0631: type mismatch in closure arguments (falscher Parametertyp)
E0631 bedeutet: Dein Closure (eine anonyme Funktion) hat die richtige Anzahl von Parametern, aber mindestens einer hat den falschen Typ. In neun von zehn Fällen ist der Unterschied ein einzelnes &: Du hast |x: i32| geschrieben, aber der Iterator leiht dir ein &i32. Die schnellste Lösung ist oft, die Typangabe einfach zu löschen und Rust den Typ selbst herausfinden zu lassen. Diese Seite zeigt dir zuerst diese Lösung und erklärt dann, woher das & kommt – damit du es beim nächsten Mal vorhersehen kannst. Die anderen Fehler dieser Reihe findest du unter Rust-Compilerfehler.
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0631]: type mismatch in closure arguments --> src/main.rs:3:43 |3 | let doubled: Vec<i32> = prices.iter().map(|p: i32| p * 2).collect(); // ❌ die Elemente sind &i32 | ^^^ -------- found signature defined here | | | expected due to this | = note: expected closure signature `fn(&{integer}) -> _` found closure signature `fn(i32) -> _`note: required by a bound in `map`help: consider adjusting the signature so it borrows its argument |3 | let doubled: Vec<i32> = prices.iter().map(|p: &i32| p * 2).collect(); // ❌ die Elemente sind &i32 | +„Type mismatch in closure arguments“ heißt so viel wie „Typen der Closure-Argumente passen nicht“. Das help schlägt vor, die Signatur so anzupassen, dass das Closure sein Argument ausleiht. Dieses Programm erzeugt den Fehler:
fn main() { let prices = vec![120, 45, 300]; let doubled: Vec<i32> = prices.iter().map(|p: i32| p * 2).collect(); // ❌ die Elemente sind &i32 println!("{:?}", doubled);}Direkt danach gibt rustc noch einen zweiten, längeren Fehler aus: the method `collect` exists for struct `Map<...>`, but its trait bounds were not satisfied (E0599). Der ist nur eine Folge davon. Behebst du E0631, verschwindet er.
Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Die beiden note-Zeilen sind das Herzstück der Meldung. Lies sie als „die Methode wollte dies, du hast das geschrieben“:
- expected
fn(&{integer}) -> _(erwartet):mapruft dein Closure mit einer Referenz auf eine Ganzzahl auf.{integer}heißt „irgendein Ganzzahltyp, den Rust noch nicht festgelegt hat“, und_heißt „beliebiger Rückgabetyp“. - found
fn(i32) -> _(gefunden): Dein Closure sagt, es nimmt eini32als Wert.
Leg beide nebeneinander, und der einzige Unterschied ist das &. Die help:-Zeile zeigt dir die Lösung mit einem einzigen Zeichen.
Denk an eine Garderobe. Du gibst deinen Mantel ab und bekommst eine Marke zurück. .iter() funktioniert genauso: Es gibt dir nicht die Elemente selbst, sondern Marken dafür (Referenzen). Ein Closure, das unbedingt den Mantel selbst haben will, nimmt keine Marke an.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Jede Iterator-Methode legt genau fest, welchen Typ sie an dein Closure übergibt. Und dieser Typ hängt davon ab, wie du den Iterator erzeugt hast:
v.iter()leiht jedes Element aus:&T.v.into_iter()(auf einemVec, der dir gehört) gibt jedes Element weg:T.filterundfindleihen dir das Element noch einmal aus. Auf.iter()bekommst du also&&T.sort_byleiht dir zwei Elemente aus:&Tund&T.
Lässt du die Parametertypen weg, setzt Rust sie anhand der Methode ein, und dann passen sie immer. Schreibst du selbst einen Typ hin, nimmt Rust dich beim Wort und prüft ihn. Rust kopiert den Wert nicht heimlich und macht aus einer Referenz keinen eigenen Wert, denn das würde ändern, was dein Code tut (und bei einem String kostet eine Kopie Speicher und Zeit). Die Lektion Iteratoren zeigt die drei Wege zu einem Iterator, und Borrowing (Ausleihen) erklärt Referenzen.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Die Typangabe entfernen
Abschnitt betitelt „Lösung 1: Die Typangabe entfernen“Probier diese Lösung zuerst. Closure-Parameter brauchen fast nie einen Typ: Rust weiß genau, was map, filter und sort_by übergeben. Lösch : i32, und der Code kompiliert:
fn main() { let prices = vec![120, 45, 300]; let ages = vec![12, 34, 17, 50]; let mut scores = vec![42, 7, 99, 15];
// keine Typangaben: Rust nimmt, was der Iterator wirklich liefert let doubled: Vec<i32> = prices.iter().map(|p| p * 2).collect(); let adults: Vec<&i32> = ages.iter().filter(|age| **age >= 18).collect(); scores.sort_by(|a, b| b.cmp(a));
println!("{:?}", doubled); println!("{:?}", adults); println!("{:?}", scores);}Ausgabe:
[240, 90, 600][34, 50][99, 42, 15, 7]p * 2 funktioniert mit einem &i32, weil die Rechenoperatoren in Rust auch Referenzen annehmen. In filter ist age ein &&i32, deshalb folgt **age beiden Referenzen bis zur Zahl. Vergleiche wie b.cmp(a) funktionieren direkt mit Referenzen.
Lösung 2: Den Typ hinschreiben, den die Methode wirklich übergibt
Abschnitt betitelt „Lösung 2: Den Typ hinschreiben, den die Methode wirklich übergibt“Nimm diese Lösung, wenn der Typ sichtbar bleiben soll, zum Beispiel damit eine lange Kette leichter zu lesen ist. Folge dem help: von rustc und ergänze das &. Du kannst das & auch ins Muster schreiben: |&p| nimmt die Referenz auseinander und gibt dir eine einfache Kopie der Zahl (das klappt bei Copy-Typen wie Ganzzahlen):
fn main() { let prices = vec![120, 45, 300]; let ages = vec![12, 34, 17, 50]; let mut scores = vec![42, 7, 99, 15];
// die echten Typen hinschreiben: & bei .iter(), && in filter let doubled: Vec<i32> = prices.iter().map(|p: &i32| p * 2).collect(); let adults: Vec<&i32> = ages.iter().filter(|age: &&i32| **age >= 18).collect(); scores.sort_by(|a: &i32, b: &i32| b.cmp(a));
// oder die Zahl mit einem &-Muster herauskopieren let tax: Vec<i32> = prices.iter().map(|&p| p / 10).collect();
println!("{:?}", doubled); println!("{:?}", adults); println!("{:?}", scores); println!("{:?}", tax);}Ausgabe:
[240, 90, 600][34, 50][99, 42, 15, 7][12, 4, 30]Achte auf den Unterschied: |p: &i32| heißt „p ist eine Referenz“, während |&p| heißt „ich bekomme eine Referenz, gib mir, was dahintersteckt“.
Lösung 3: Ändern, was der Iterator herausgibt
Abschnitt betitelt „Lösung 3: Ändern, was der Iterator herausgibt“Nimm diese Lösung, wenn du wirklich die Werte willst und keine Referenzen. Dafür gibt es zwei Wege:
.iter().copied()macht aus jedem&i32eini32. Das nimmst du für kleineCopy-Typen wie Zahlen,charundbool. (.cloned()macht dasselbe für Typen wieString, indem es klont.).into_iter()auf einemVecgibt die Elemente selbst heraus. Danach ist derVecverbraucht. Nimm das also, wenn du ihn nicht mehr brauchst.
fn main() { let prices = vec![120, 45, 300];
// .copied() macht aus &i32-Elementen i32-Elemente let doubled: Vec<i32> = prices.iter().copied().map(|p: i32| p * 2).collect(); println!("{:?}", doubled);
// .into_iter() auf einem Vec gibt die Elemente selbst heraus (und verbraucht den Vec) let names = vec![String::from("ferris"), String::from("corro")]; let loud: Vec<String> = names.into_iter().map(|name: String| name.to_uppercase()).collect(); println!("{:?}", loud);}Ausgabe:
[240, 90, 600]["FERRIS", "CORRO"]Brauchst du names danach noch, bleib bei .iter() und nimm stattdessen &String. Wenn du Werte aus einem Vec herausbewegen willst, den du nur ausgeliehen hast, ist das ein anderer Fehler: E0507.
Lösung 4: Eine benannte Funktion nehmen lassen, was der Iterator liefert
Abschnitt betitelt „Lösung 4: Eine benannte Funktion nehmen lassen, was der Iterator liefert“Nimm diese Lösung, wenn du eine Funktion über ihren Namen übergibst, etwa .map(shout). Dann steht in der Meldung „function“ statt „closure“:
error[E0631]: type mismatch in function arguments --> src/main.rs:7:51 |1 | fn shout(word: &String) -> String { | --------------------------------- found signature defined here...7 | let loud: Vec<String> = words.into_iter().map(shout).collect(); | --- ^^^^^ expected due to this | | | required by a bound introduced by this call | = note: expected function signature `fn(&str) -> _` found function signature `fn(&String) -> _`Die Elemente sind &str, aber shout will &String. Eine gute Gewohnheit: Lass Funktionen, die Text nur lesen, &str nehmen. Dann akzeptieren sie String-Literale, &String und mehr. Wenn der Iterator ein zusätzliches & hinzufügt (wie filter), pack den Aufruf in ein kurzes Closure. In einem normalen Funktionsaufruf entfernt Rust überzählige & für dich (das nennt man Deref Coercion), aber nicht, wenn du die Funktion über ihren Namen übergibst:
// &str nehmen: funktioniert mit String-Literalen, &String und mehrfn shout(word: &str) -> String { word.to_uppercase()}
fn is_long(word: &str) -> bool { word.len() > 4}
fn main() { let words = vec!["crab", "rustacean", "ferris"];
// die Elemente sind &str, also lässt sich shout über den Namen übergeben let loud: Vec<String> = words.iter().copied().map(shout).collect(); println!("{:?}", loud);
// filter liefert &&str: ein kleines Closure lässt Rust die Umwandlung übernehmen let long: Vec<&str> = words.iter().copied().filter(|w| is_long(w)).collect(); println!("{:?}", long);}Ausgabe:
["CRAB", "RUSTACEAN", "FERRIS"]["rustacean", "ferris"]Die Wörter bleiben hier auf Englisch, weil is_long mit .len() die Bytes zählt und Umlaute zwei Bytes belegen. Mehr zu &str und String findest du unter Strings.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Sortieren mit Typangaben. scores.sort_by(|a: i32, b: i32| b.cmp(&a)) ergibt E0631 mit expected closure signature `for<'a, 'b> fn(&'a {integer}, &'b {integer}) -> _`. Der Teil for<'a, 'b> sagt nur „für jede beliebige Ausleihe“. Lösung: |a, b| b.cmp(a). Siehe Sortieren und Suchen.
Map-Einträge mit eigenen Typen. stock.iter().map(|(name, count): (String, i32)| ...) auf einer HashMap<String, i32> ergibt expected closure signature `fn((&String, &_)) -> _`. .iter() auf einer Map leiht Schlüssel und Wert aus. Lösung: Lass die Annotation weg, oder schreib (&String, &i32).
Deine eigene generische Funktion. Bekommt fn apply<F: Fn(i32) -> i32>(f: F, x: i32) ein Closure |x: f64| x / 2.0, meldet rustc expected closure signature `fn(i32) -> _` und found closure signature `fn(f64) -> _` und zeigt auf den Bound. Lösung: Lass das Closure i32 nehmen (und wandle innen um, x as f64 / 2.0), oder ändere den Bound, wenn du wirklich f64 meintest. Siehe Closure-Traits.
for_each mit String. words.iter().for_each(|w: String| ...) auf einem Vec<&str> erwartet fn(&&str). Ein &str ist kein String. Lösung: |w|, oder |w: &&str|.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0593: closure takes the wrong number of arguments: der Schwesterfehler, bei dem die Anzahl der Parameter nicht stimmt (zum Beispiel
|a, b|, wo ein Tupel übergeben wird). - E0057: closure called with the wrong number of arguments: Du rufst ein Closure selbst auf und übergibst zu wenige oder zu viele Werte.
- E0308: mismatched types: der allgemeine Fehler „falscher Typ“, zum Beispiel wenn der Rückgabewert des Closures den falschen Typ hat, oder bei einem
fn(...)-Zeiger als Parameter. - E0599: no method found: der Fehler „trait bounds were not satisfied“ bei
collectodersum, der auf E0631 folgt. Er verschwindet zusammen mit E0631. - E0507: cannot move out of a borrowed value: wenn du die Ownership von Elementen übernehmen willst, die du mit
.iter()nur ausgeliehen hast. - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. let v = vec![1, 2, 3]; Welchen Typ bekommt das Closure in v.iter().map(|x| ...)?
2. Die Note sagt expected closure signature fn(&{integer}) -> _, found closure signature fn(i32) -> _. Was ist die schnellste Lösung?
3. Was macht |&p| p * 2 in prices.iter().map(...)?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0631 – Index der Rust-Fehlercodes (auf Englisch): die offizielle Erklärung dieses Fehlers.
- Eine Reihe von Elementen mit Iteratoren verarbeiten – Das Rust-Buch:
iter,into_iterunditer_mutund was jede dieser Methoden herausgibt. - Closures – Das Rust-Buch: wie die Parametertypen von Closures abgeleitet werden und wann du sie selbst hinschreibst.
Iterator::copied– Doku der Standardbibliothek (auf Englisch): macht aus einem Iterator über&Teinen Iterator überT.slice::sort_by– Doku der Standardbibliothek (auf Englisch): die Signatur, die zeigt, dass das Vergleichs-Closure zwei Referenzen bekommt.