Zum Inhalt springen

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.

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.

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.

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.

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: 2
▶ Selbst ausprobieren

Ohne 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"]
▶ Selbst ausprobieren

Ä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.

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 mutable
4 | click(); // ❌ cannot borrow `click` as mutable
| ----- cannot borrow as mutable
5 | 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: 3
▶ Selbst ausprobieren

Das ü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.

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]
▶ Selbst ausprobieren

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.

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.

  • E0384: cannot assign twice to immutable variable (zweimalige Zuweisung an eine unveränderbare Variable). Dasselbe fehlende mut, aber bei x = ... oder x += 1 statt bei einem Methodenaufruf.
  • E0594: Zuweisung an ein Feld oder über eine &-Referenz ist nicht möglich, zum Beispiel p.hp = 5;, wenn p nicht mut ist, oder *x += 1 in for x in &v. Dieselben Lösungen: let mut oder &mut.
  • E0308: mismatched types (unpassende Typen), types differ in mutability – wenn du &x an eine Funktion übergibst, die &mut x will.
  • 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

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