Rust-Fehler E0368: binary assignment operation += cannot be applied to type
E0368 bedeutet: Du hast einen zusammengesetzten Operator wie +=, -= oder *= auf einen Typ angewendet, der ihn noch nicht unterstützt. Er ist der +=-Cousin von E0369. Deine eigenen Structs bekommen += nicht automatisch, nicht einmal, wenn sie + schon können. Die Lösung ist meist ein kurzer impl AddAssign-Block, und bei Zahlen hinter einer Referenz ist es ein einziges *. Die anderen Fehler dieser Reihe findest du unter Rust-Compilerfehler.
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0368]: binary assignment operation `+=` cannot be applied to type `Money` --> src/main.rs:8:5 |8 | wallet += tip; // ❌ Money doesn't know how to do += | ------^^^^^^^ | | | cannot use `+=` on type `Money` |note: an implementation of `AddAssign` might be missing for `Money` --> src/main.rs:1:1 |1 | struct Money { | ^^^^^^^^^^^^ must implement `AddAssign`Dieses Programm erzeugt den Fehler:
struct Money { cents: i64,}
fn main() { let mut wallet = Money { cents: 500 }; let tip = Money { cents: 150 }; wallet += tip; // ❌ Money doesn't know how to do += println!("{}", wallet.cents);}Der Kommentar sagt: Money weiß nicht, wie += geht. Der Operator in der ersten Zeile der Meldung ändert sich, aber es ist derselbe Fehler. p -= step verlangt SubAssign, und p *= 2 verlangt MulAssign<{integer}>, also „mit einer ganzen Zahl multiplizieren und zuweisen“.
Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Die Beschriftung unter der linken Seite sagt, was schiefgelaufen ist: „cannot use += on type Money“ (+= kann auf den Typ Money nicht angewendet werden). Die note: zeigt dann auf dein Struct und sagt, es muss AddAssign implementieren. Das ist der Trait (eine Art Fähigkeit) hinter +=.
Stell dir ein Sparschwein vor. Eine Münze einwerfen ist eine klare Sache. Aber wenn dir jemand eine Kiste mit der Aufschrift „Geld“ gibt und sagt: „Füg diese andere Kiste hinzu“, musst du wissen, wie: die Cents zusammenzählen? die Kisten stapeln? Rust will, dass du diese Regel einmal aufschreibst.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Jeder zusammengesetzte Operator ist ein versteckter Methodenaufruf. a += b ruft AddAssign::add_assign(&mut a, b) auf, a -= b ruft SubAssign::sub_assign auf, und so weiter. Alle diese Traits stehen in std::ops. Zahlen implementieren sie schon. Dein neues Struct startet ohne einen einzigen davon, und für Rechenoperatoren gibt es kein #[derive].
Jetzt kommt der Teil, der viele überrascht: Add und AddAssign sind getrennte Traits. Selbst wenn Point schon Add implementiert, ergibt p += q trotzdem E0368. Der Grund: Die beiden erledigen verschiedene Aufgaben. a + b nimmt zwei Werte und gibt einen neuen zurück. a += b ändert a direkt an Ort und Stelle über &mut self und gibt nichts zurück. Das direkte Ändern kann viel günstiger sein: += bei einem String hängt an den vorhandenen Speicher an, statt einen neuen String zu bauen. Rust lässt jeden Typ selbst wählen und macht deshalb nie heimlich aus += ein +.
Operator-Traits werden unter Wichtige Traits und Fortgeschrittene Traits erklärt.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: AddAssign (und seine Geschwister) implementieren
Abschnitt betitelt „Lösung 1: AddAssign (und seine Geschwister) implementieren“Nimm das, wenn += für deinen Typ eine klare Bedeutung hat, etwa Geld hinzufügen oder eine Position verschieben. Du schreibst eine Methode, die self ändert. SubAssign, MulAssign und die anderen fügst du genauso hinzu, wenn du sie brauchst:
use std::ops::{AddAssign, SubAssign};
#[derive(Debug)]struct Money { cents: i64,}
impl AddAssign for Money { fn add_assign(&mut self, other: Money) { self.cents += other.cents; }}
impl SubAssign for Money { fn sub_assign(&mut self, other: Money) { self.cents -= other.cents; }}
fn main() { let mut wallet = Money { cents: 500 }; wallet += Money { cents: 150 }; // Trinkgeld bekommen wallet -= Money { cents: 200 }; // Mittagessen gekauft println!("{:?}", wallet);}Ausgabe:
Money { cents: 450 }Beachte &mut self und den fehlenden Rückgabetyp. Das ist der ganze Unterschied zu Add. Innen funktioniert self.cents += other.cents, weil i64 schon AddAssign hat.
Lösung 2: Du hast schon Add? Schreib a = a + b oder nutz es wieder
Abschnitt betitelt „Lösung 2: Du hast schon Add? Schreib a = a + b oder nutz es wieder“Nimm das, wenn dein Typ schon Add implementiert (vielleicht nachdem du E0369 behoben hast). Ist der Typ Copy, kannst du einfach pos = pos + step schreiben. Das braucht nur Add. Oder du implementierst AddAssign in einer Zeile, indem du dein Add aufrufst. Dann funktioniert auch +=:
use std::ops::{Add, AddAssign};
#[derive(Debug, Clone, Copy)]struct Point { x: i32, y: i32,}
impl Add for Point { type Output = Point;
fn add(self, other: Point) -> Point { Point { x: self.x + other.x, y: self.y + other.y } }}
impl AddAssign for Point { fn add_assign(&mut self, other: Point) { *self = *self + other; // Add wiederverwenden }}
fn main() { let mut pos = Point { x: 0, y: 0 }; let step = Point { x: 1, y: 2 }; pos = pos + step; // braucht nur Add pos += step; // braucht AddAssign println!("{:?}", pos);}Ausgabe:
Point { x: 2, y: 4 }*self = *self + other kopiert den aktuellen Wert heraus, addiert und schreibt das Ergebnis zurück. Das klappt nur, weil Point Copy ist. Für einen Typ, der Daten auf dem Heap besitzt (etwa ein Vec-Feld), schreibst du stattdessen die direkte Version aus Lösung 1.
Lösung 3: Strings: s += &t oder push_str
Abschnitt betitelt „Lösung 3: Strings: s += &t oder push_str“Nimm das, wenn links ein String steht. += funktioniert bei einem eigenen String, und rechts muss ein &str stehen. Zwei Fälle bringen Leute oft durcheinander:
cannot be applied to type `&str`: Links steht ein String-Slice wie"Hello, ". Ein&strist eine reine Leseansicht, er kann nicht wachsen. Mach zuerst einenStringdaraus.s += t, wobeitauch einStringist, ist nicht E0368. Es ist E0308, „expected&str, foundString“, mit der Hilfes += &t. Leih dir einfach die rechte Seite aus.
fn main() { let mut greeting = String::from("Hallo, "); // eigener String, kann also wachsen let name = String::from("Ferris");
greeting += &name; // rechte Seite ausleihen greeting.push_str("!"); // dasselbe als Methode
let mut tag = "Krabbe".to_string(); // &str links? Mach einen String daraus tag += " #1";
println!("{greeting}"); println!("{tag}"); println!("{name} ist noch benutzbar");}Ausgabe:
Hallo, Ferris!Krabbe #1Ferris ist noch benutzbarWeil die rechte Seite nur ausgeliehen ist, gehört dir name danach immer noch. += und push_str machen genau dasselbe; nimm, was sich besser liest. Siehe Strings.
Lösung 4: Eine Bedingung T: AddAssign in generischem Code
Abschnitt betitelt „Lösung 4: Eine Bedingung T: AddAssign in generischem Code“Nimm das, wenn der Fehler cannot be applied to type `T` lautet. In einer generischen Funktion könnte T jeder Typ sein, auch einer ohne +=. Die Hilfe von rustc sagt genau, was du hinzufügen musst: consider further restricting type parameter `T` with trait `AddAssign` (schränke den Typparameter T zusätzlich mit dem Trait AddAssign ein).
use std::ops::AddAssign;
fn total<T>(items: &[T]) -> Twhere T: Copy + Default + AddAssign,{ let mut sum = T::default(); for &item in items { sum += item; } sum}
fn main() { println!("{}", total(&[1, 2, 3])); println!("{}", total(&[0.5, 0.25]));}Ausgabe:
60.75Jetzt funktioniert total für jeden Typ, der sich kopieren lässt, einen Startwert hat und += unterstützt: Ganzzahlen, Gleitkommazahlen und auch dein eigenes Money, sobald es diese Traits implementiert. Für einfache Zahlen erledigt items.iter().sum() dasselbe in einer Zeile. Siehe Generics.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Zählen mit einer HashMap. let count = counts.entry(word).or_insert(0); und danach count += 1; ergibt cannot be applied to type `&mut i32`. count ist eine Referenz, nicht die Zahl selbst. Die Hilfe von rustc: „+= can be used on i32 if you dereference the left-hand side“ (+= geht bei i32, wenn du die linke Seite dereferenzierst). Lösung: *count += 1;. Siehe HashMap.
Ein Struct mit einer Zahl skalieren. p *= 2 braucht MulAssign<{integer}>, denn rechts steht eine Zahl, kein weiterer Point. Lösung: Implementiere den Trait für genau diesen Typ auf der rechten Seite:
impl std::ops::MulAssign<i32> for Point { fn mul_assign(&mut self, factor: i32) { self.x *= factor; self.y *= factor; }}Laufende Summen in einer Schleife. total += item.price, wobei price dein eigener Typ Money ist. Mit Lösung 1 funktioniert das. Möchtest du lieber prices.iter().copied().sum() schreiben, brauchst du stattdessen den Trait Sum. Ohne ihn bekommst du E0277 („a value of type Money cannot be made by summing an iterator“).
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0369: binary operation cannot be applied: dasselbe Problem bei
+,==,<und den anderen Operatoren ohne Zuweisung. - E0308: mismatched types: Das bekommst du bei
s += tmit zweiStrings (leih dir die rechte Seite aus:s += &t). - E0277: trait bound not satisfied:
+=gibt es für den linken Typ, aber nicht mit diesem Typ rechts, zum Beispielcannot add-assign `{float}` to `{integer}`beitotal += 2.5, wenntotaleine Ganzzahl ist. - E0384: cannot assign twice:
x += 1auf einer Variable, die ohnemutdeklariert ist. Der Operator ist in Ordnung, nur die Variable ist nicht veränderbar. - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. Point implementiert Add, aber p += q ergibt trotzdem E0368. Warum?
2. count kommt von map.entry(word).or_insert(0). Welche Zeile kompiliert?
3. s und t sind beide String. Wie hängst du t an s an?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0368 – Index der Rust-Fehlercodes (auf Englisch): die offizielle Erklärung dieses Fehlers.
std::ops::AddAssign– Doku der Standardbibliothek (auf Englisch): der Trait hinter+=, mit Beispiel;SubAssign,MulAssignund die anderen stehen instd::ops.- Generische Standardparameter und Operatorüberladung – Die Programmiersprache Rust: Operatoren mit
std::opsüberladen und Standard-Typparameter wieRhs. - UTF-8-kodierten Text in Strings ablegen – Die Programmiersprache Rust: warum Strings mit
push_strwachsen und+rechts einen&strnimmt. - Operator Overloading – Rust by Example (auf Englisch): ein kurzes, ausführbares Beispiel, wie du Operator-Traits für deine eigenen Typen implementierst.