Rust-Fehler E0596: cannot borrow as mutable, as it is not declared as mutable
E0596 bedeutet: Ein Stück Code will einen Wert ändern (es braucht einen veränderbaren Borrow, &mut), aber du hast ihm nur erlaubt, ihn zu lesen. Meistens hast du mut an einem let vergessen, oder eine Funktion nimmt &, wo sie &mut braucht. Der Fehler ist sehr häufig, und die help:-Zeile des Compilers sagt dir fast immer genau, wo das mut hingehört. Diese Seite zeigt dir zuerst die Lösung und erklärt sie dann. Die anderen Fehler dieser Reihe findest du unter Rust-Compilerfehler.
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0596]: cannot borrow `tasks` as mutable, as it is not declared as mutable --> src/main.rs:3:5 |3 | tasks.push("write tests"); // ❌ push muss `tasks` ändern | ^^^^^ cannot borrow as mutable |help: consider changing this to be mutable |2 | let mut tasks = Vec::new(); | +++Auf Deutsch heißt das: „tasks kann nicht veränderbar ausgeliehen werden, weil es nicht als veränderbar deklariert ist.“ Dieses Programm erzeugt die Meldung:
fn main() { let tasks = Vec::new(); tasks.push("write tests"); // ❌ push muss `tasks` ändern println!("{tasks:?}");}Du begegnest auch einer zweiten Formulierung: „cannot borrow *tasks as mutable, as it is behind a & reference“ (kann nicht veränderbar ausgeliehen werden, weil es hinter einer &-Referenz liegt). Derselbe Fehlercode, eine etwas andere Ursache: Dort ist die Variable eine &-Referenz, also kannst du nicht ändern, worauf sie zeigt. Beide Fälle behandeln wir unten.
Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Schau dir die ^^^^^ unter tasks in Zeile 3 an. Um ein Element hinzuzufügen, braucht Vec::push einen veränderbaren Borrow des Vektors. Seine Signatur lautet fn push(&mut self, value: T). Aber tasks wurde mit einem einfachen let angelegt und ist deshalb schreibgeschützt. Rust kann kein &mut auf etwas herausgeben, von dem du gesagt hast, dass es sich nie ändert.
Die help:-Zeile zeigt die Lösung: let mut tasks. Die +++ markieren die drei Zeichen, die du ergänzen musst.
Stell dir ein geteiltes Dokument vor, auf das du nur Lesezugriff hast. Du kannst jedes Wort lesen, aber der Knopf „Bearbeiten“ ist ausgegraut. Mit mut (oder &mut) bittest du um Schreibzugriff.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“In Rust sind Variablen standardmäßig unveränderbar, und Referenzen sind standardmäßig schreibgeschützt. Jede Änderung musst du ausdrücklich erlauben: let mut für eine Variable, die dir gehört, &mut für eine Referenz auf etwas, das du ausgeliehen hast. Siehe Variablen und Borrowing (Ausleihen).
Dadurch kannst du Code leichter vertrauen. Nimmt eine Funktion &Vec<String>, weißt du schon an ihrer Signatur, dass sie deine Liste nicht ändert. Nimmt sie &mut Vec<String>, weißt du, dass sie es könnte. Nichts ändert sich hinter deinem Rücken.
Dieselbe Regel macht Rusts größere Sicherheitsgarantien erst möglich. Weil „darf sich ändern“ immer hingeschrieben ist, kann der Compiler sicherstellen, dass nie gleichzeitig jemand schreibt und jemand liest (darum geht es bei E0502 und E0499). E0596 ist der erste Schritt: Du musst erst um Schreibzugriff bitten, bevor überhaupt jemand prüft, ob das Schreiben sicher ist.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: mut an die Variable schreiben
Abschnitt betitelt „Lösung 1: mut an die Variable schreiben“Nimm diese Lösung, wenn der Wert dir gehört und sich ändern soll: ein Vec, in das du etwas einfügst, ein String, an den du etwas anhängst, oder ein Struct, dessen &mut self-Methode du aufrufst. Meistens ist das die richtige Lösung.
struct Counter { count: u32,}
impl Counter { fn increment(&mut self) { self.count += 1; }}
fn main() { let mut tasks = Vec::new(); tasks.push("Tests schreiben"); tasks.push("Bug beheben"); println!("{tasks:?}");
let mut counter = Counter { count: 0 }; counter.increment(); counter.increment(); println!("Zähler: {}", counter.count);}Ausgabe:
["Tests schreiben", "Bug beheben"]Zähler: 2Ohne mut an counter schlägt counter.increment() mit cannot borrow counter as mutable fehl. Eine Methode, die &mut self nimmt, leiht die ganze Variable veränderbar aus – also muss die Variable mut sein. Methoden, die nur &self nehmen, funktionieren auch ohne. Siehe Verhalten mit Methoden hinzufügen.
Das Gleiche gilt für einen Parameter, der der Funktion gehört: fn shout(s: String) kann s.push('!') nicht aufrufen, fn shout(mut s: String) dagegen schon.
Lösung 2: Den Parameter zu &mut ändern und &mut x übergeben
Abschnitt betitelt „Lösung 2: Den Parameter zu &mut ändern und &mut x übergeben“Nimm diese Lösung, wenn eine Funktion etwas ändern muss, das dem Aufrufer gehört. Diese Variante liefert die Formulierung mit „behind a & reference“:
fn add_task(tasks: &Vec<String>, task: &str) { tasks.push(task.to_string()); // ❌ `tasks` ist eine `&`-Referenz}error[E0596]: cannot borrow `*tasks` as mutable, as it is behind a `&` reference --> src/main.rs:2:5 |2 | tasks.push(task.to_string()); // ❌ `tasks` ist eine `&`-Referenz | ^^^^^ `tasks` is a `&` reference, so it cannot be borrowed as mutable |help: consider changing this to be a mutable reference |1 | fn add_task(tasks: &mut Vec<String>, task: &str) { | +++Der Compiler erklärt: tasks ist eine &-Referenz und kann deshalb nicht veränderbar ausgeliehen werden. Du behebst das an drei Stellen: Der Parametertyp wird zu &mut Vec<String>, die Variable des Aufrufers wird zu let mut, und der Aufruf übergibt &mut tasks:
fn add_task(tasks: &mut Vec<String>, task: &str) { tasks.push(task.to_string());}
fn main() { let mut tasks = Vec::new(); add_task(&mut tasks, "Tests schreiben"); add_task(&mut tasks, "Doku aktualisieren"); println!("{} Aufgaben: {tasks:?}", tasks.len());}Ausgabe:
2 Aufgaben: ["Tests schreiben", "Doku aktualisieren"]Änderst du den Parameter, übergibst aber weiterhin &tasks, bekommst du stattdessen E0308 mit dem Hinweis types differ in mutability (die Typen unterscheiden sich in der Veränderbarkeit). Das heißt nur: Schreib auch beim Aufruf &mut tasks. Dass du &mut beim Aufruf hinschreiben musst, ist Absicht: Jeder, der main liest, sieht sofort, dass add_task die Variable tasks ändern darf.
Lösung 3: Das Closure mit let mut binden
Abschnitt betitelt „Lösung 3: Das Closure mit let mut binden“Nimm diese Lösung, wenn ein Closure eine eingefangene Variable ändert. So ein Closure ändert bei jedem Aufruf seinen eigenen Zustand. Deshalb braucht der Aufruf einen veränderbaren Borrow des Closures selbst:
fn main() { let mut clicks = 0; let click = || clicks += 1; click(); // ❌ cannot borrow `click` as mutable click(); println!("Clicks: {clicks}");}error[E0596]: cannot borrow `click` as mutable, as it is not declared as mutable --> src/main.rs:3:9 |3 | let click = || clicks += 1; | ^^^^^ ------ calling `click` requires mutable binding due to mutable borrow of `clicks` | | | not mutable4 | click(); // ❌ cannot borrow `click` as mutable | ----- cannot borrow as mutable5 | click(); | ----- cannot borrow as mutable |help: consider changing this to be mutable |3 | let mut click = || clicks += 1; | +++Die Meldung sagt: Um click aufzurufen, braucht es eine veränderbare Bindung, weil das Closure clicks veränderbar ausleiht. Die Variable clicks ist schon mut. Was fehlt, ist das mut am Closure:
fn main() { let mut clicks = 0; let mut click = || clicks += 1; click(); click(); click(); println!("Klicks: {clicks}");}Ausgabe:
Klicks: 3Das überrascht viele, denn das Closure „sieht“ nicht so aus, als würde es sich ändern. Aber es hält intern ein &mut clicks. Damit ist es ein FnMut-Closure, und um ein FnMut aufzurufen, brauchst du mut. Siehe Closures, die eingefangene Variablen ändern und Fn, FnMut und FnOnce.
Lösung 4: iter_mut() oder &mut in der Schleife
Abschnitt betitelt „Lösung 4: iter_mut() oder &mut in der Schleife“Nimm diese Lösung, wenn du über eine Sammlung läufst und jedes Element ändern willst. .iter() und for x in &list liefern &-Referenzen, und die sind schreibgeschützt:
for name in names.iter() { name.push('!'); // ❌ cannot borrow `*name` as mutable, as it is behind a `&` reference}Wechsle zu .iter_mut() oder &mut names (und mach names selbst mut):
fn main() { let mut names = vec![String::from("ferris"), String::from("corro")]; for name in names.iter_mut() { name.push('!'); } println!("{names:?}");
let mut prices = vec![10, 20, 30]; for price in &mut prices { *price += 5; } println!("{prices:?}");}Ausgabe:
["ferris!", "corro!"][15, 25, 35]Das schlägt dir auch der Compiler vor: help: use mutable method ... names.iter_mut(). Dasselbe gilt für Closures in Iterator-Ketten: words.iter().for_each(|w| w.push('!')) schlägt fehl, words.iter_mut().for_each(|w| w.push('!')) funktioniert. Siehe Iteratoren.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Einen Vec oder String Schritt für Schritt aufbauen. let list = Vec::new(); und dann list.push(...), oder let s = String::new(); und dann s.push_str(...). Lösung: let mut. Hast du alle Elemente schon am Anfang, braucht let list = vec![a, b, c]; überhaupt kein mut.
Eine „Setter“- oder „Update“-Methode an einem Struct aufrufen. account.deposit(50) schlägt fehl, wenn deposit &mut self nimmt und du nicht let mut account geschrieben hast. Lösung: let mut account = Account::new();. Siehe Structs.
Hilfsfunktionen, die aus Gewohnheit & nehmen. Du schreibst fn reset(board: &Board) und versuchst dann, board darin zu ändern. Lösung: Mach daraus board: &mut Board und ruf reset(&mut board) auf. Eine gute Gewohnheit: Schreib &, wenn die Funktion nur liest, und &mut, wenn sie etwas ändert.
Einen Wert in einer HashMap ändern. if let Some(v) = map.get("key") { v.push(..) } schlägt fehl, weil get eine &-Referenz zurückgibt. Lösung: Nimm map.get_mut("key") oder die entry-API. Siehe HashMaps.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0384: cannot assign twice to immutable variable (zweimalige Zuweisung an eine unveränderbare Variable). Dasselbe fehlende
mut, aber beix = ...oderx += 1statt bei einem Methodenaufruf. - E0594: Zuweisung an ein Feld oder über eine
&-Referenz ist nicht möglich, zum Beispielp.hp = 5;, wennpnichtmutist, oder*x += 1infor x in &v. Dieselben Lösungen:let mutoder&mut. - E0308: mismatched types (unpassende Typen),
types differ in mutability– wenn du&xan eine Funktion übergibst, die&mut xwill. - E0502: Du hast
&mut-Zugriff bekommen, aber etwas anderes liest den Wert noch. - E0499: zwei
&mut-Borrows desselben Werts zur gleichen Zeit. - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. let scores = Vec::new(); scores.push(10); ergibt E0596. Wie lautet die Lösung?
2. Eine Funktion fn add(list: &Vec<u32>) ruft list.push(1) auf. Was änderst du?
3. let mut total = 0; let add = |x| total += x; add(5); schlägt fehl. Warum?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0596 – Rust error codes index (auf Englisch): die offizielle Erklärung dieses Fehlers.
- Referenzen und Borrowing – Das Rust-Buch: der Abschnitt „Veränderbare Referenzen“ erklärt
&mutund warum du ausdrücklich darum bitten musst. - Methoden – Das Rust-Buch:
&selfim Vergleich zu&mut self, und was beide vom Aufrufer brauchen. - Closures – Das Rust-Buch: wie Closures Variablen einfangen und warum ein Closure, das etwas ändert,
mutsein muss. - Mutability (borrowing) – Rust by Example (auf Englisch): lauffähige Beispiele für
&- und&mut-Borrows nebeneinander.