Rust-Fehler E0434: can't capture dynamic environment in a fn item (innere fn)
E0434 bedeutet: Eine Funktion, die in einer anderen Funktion steht, will eine lokale Variable der äußeren Funktion benutzen. Es sieht aus, als müsste das gehen: Die Variable steht ja direkt da, ein paar Zeilen weiter oben. Aber ein verschachteltes fn ist in Rust ein eigenes Element, und es sieht die Variablen um sich herum nicht. Die Formulierung des Compilers, „dynamic environment“ (dynamische Umgebung), meint einfach „die lokalen Variablen, die existieren, während die äußere Funktion läuft“. Dieser Fehler ist einer der freundlichsten: rustc verrät dir die Lösung gleich in seiner help:-Zeile. (Diese Seite gehört zur Reihe Rust-Compilerfehler einfach erklärt.)
Die Fehlermeldung
Abschnitt betitelt „Die Fehlermeldung“error[E0434]: can't capture dynamic environment in a fn item --> src/main.rs:5:24 |5 | price * (1.0 + tax_rate) // ❌ inneres `fn` sieht `tax_rate` nicht | ^^^^^^^^ | = help: use the `|| { ... }` closure form insteadAuf Deutsch: „Die dynamische Umgebung kann in einem fn-Element nicht eingefangen werden.“ Die Hilfe sagt: „Benutze stattdessen die Closure-Form || { ... }.“
Dieses Programm erzeugt den Fehler:
fn main() { let tax_rate = 0.2;
fn with_tax(price: f64) -> f64 { price * (1.0 + tax_rate) // ❌ inneres `fn` sieht `tax_rate` nicht }
println!("{:.2}", with_tax(10.0));}Was sie bedeutet
Abschnitt betitelt „Was sie bedeutet“Die ^^^ zeigen in Zeile 5 auf tax_rate. Das ist eine lokale Variable von main, angelegt mit let in Zeile 2. Die innere Funktion with_tax will sie lesen, und das ist nicht erlaubt.
Die help:-Zeile nennt die Lösung: Schreib ein Closure, || { ... }, statt eines verschachtelten fn. Ein Closure kann die Variablen um sich herum benutzen. Ein fn kann das nicht, egal wo du es hinschreibst.
Oft bekommst du zusätzlich eine Warnung, unused variable: `tax_rate` (unbenutzte Variable), weil aus Sicht von Rust nichts sie erlaubterweise benutzen konnte. Diese Warnung verschwindet zusammen mit dem Fehler.
Das ist wie ein versiegelter Brief, den du an deinem Schreibtisch geschrieben hast. Es ist egal, dass deine Notizen beim Schreiben auf dem Tisch lagen: Wer den Brief öffnet, sieht nur das, was du in den Umschlag gesteckt hast.
Warum Rust das macht
Abschnitt betitelt „Warum Rust das macht“Ein verschachteltes fn wird genauso kompiliert wie eines ganz oben in der Datei. Dass es in main steht, versteckt nur seinen Namen vor dem Rest des Codes. Es verbindet die Funktion nicht mit den Variablen von main, und dafür gibt es einen guten Grund: Eine normale Funktion hat keinen Platz, an dem sie sie aufbewahren könnte. Du kannst with_tax von überall aufrufen, wo main sie erreicht, sie als Funktionszeiger speichern oder sie aufrufen, nachdem die Variablen von main schon weg sind. Ein normales fn ist nur Code, ohne angehängte Daten.
Ein Closure ist Code plus die Variablen, die es eingefangen hat. Rust speichert diese Werte (oder Referenzen darauf) im Closure, und der Borrow Checker sorgt dafür, dass sie lange genug leben. Deshalb verweist rustc dich auf Closures. Siehe Variablen aus der Umgebung einfangen.
Dieselbe Regel gilt für generische Parameter: Auch das T der äußeren Funktion kann ein inneres fn nicht benutzen. Das ist E0401.
So behebst du den Fehler
Abschnitt betitelt „So behebst du den Fehler“Lösung 1: Ein Closure benutzen
Abschnitt betitelt „Lösung 1: Ein Closure benutzen“Nimm diese Lösung in den meisten Fällen, wenn die Hilfsfunktion klein ist und nur in einer Funktion gebraucht wird. Schreib let name = |params| body;, und das Closure kann jede Variable im Gültigkeitsbereich benutzen:
fn main() { let tax_rate = 0.2;
let with_tax = |price: f64| price * (1.0 + tax_rate); // ✅ ein Closure sieht `tax_rate`
println!("{:.2}", with_tax(10.0)); println!("{:.2}", with_tax(24.5));
let prices = [3.0, 7.5, 12.0]; let total: f64 = prices.iter().map(|&p| with_tax(p)).sum(); println!("Summe: {:.2}", total);}Ausgabe:
12.0029.40Summe: 27.00Ein Closure, das eine Variable nur liest, leiht sie sich aus, also kannst du tax_rate danach weiter benutzen. Änderst du die Variable im Closure, braucht das Closure let mut. Siehe Closures.
Lösung 2: Den Wert als Argument übergeben
Abschnitt betitelt „Lösung 2: Den Wert als Argument übergeben“Nimm diese Lösung, wenn die Hilfsfunktion eine echte Funktion bleiben soll: Sie ist länger, du willst sie testen, oder mehrere Funktionen benutzen sie. Gib ihr einen Parameter mehr und übergib den Wert:
fn with_tax(price: f64, tax_rate: f64) -> f64 { price * (1.0 + tax_rate) // ✅ `tax_rate` ist jetzt ein Parameter}
fn receipt(prices: &[f64], tax_rate: f64) -> f64 { prices.iter().map(|&p| with_tax(p, tax_rate)).sum()}
fn main() { let tax_rate = 0.2; println!("{:.2}", with_tax(10.0, tax_rate)); println!("Summe: {:.2}", receipt(&[3.0, 7.5, 12.0], tax_rate));
let reduced_rate = 0.05; println!("{:.2}", with_tax(10.0, reduced_rate));}Ausgabe:
12.00Summe: 27.0010.50Hier ist die Funktion auf die oberste Ebene gewandert, wo die meisten Hilfsfunktionen in Rust stehen. Verschachtelt funktioniert es aber auch; wichtig ist nur, dass der Wert als Argument hereinkommt. Bonus: Jetzt funktioniert sie mit jedem Steuersatz, nicht nur mit einem. Siehe Parameter.
Lösung 3: Ein const daraus machen
Abschnitt betitelt „Lösung 3: Ein const daraus machen“Nimm diese Lösung, wenn der Wert fest ist und schon vor dem Programmstart feststeht, wie ein Steuersatz, der im Code steht. Anders als eine let-Variable ist ein const ein Element, und jede Funktion kann es benutzen, verschachtelt oder nicht:
const TAX_RATE: f64 = 0.2; // ✅ eine Konstante, für jede Funktion sichtbar
fn main() { fn with_tax(price: f64) -> f64 { price * (1.0 + TAX_RATE) }
println!("{:.2}", with_tax(10.0)); println!("{:.2}", with_tax(24.5));}Ausgabe:
12.0029.40Das klappt nur für Werte, die sich zur Kompilierzeit berechnen lassen. Kommt der Steuersatz aus einer Datei, einer Benutzereingabe oder einer anderen Variable, reicht ein const nicht: const RATE: f64 = tax_rate; ergibt einen anderen Fehler, E0435 (attempt to use a non-constant value in a constant, Versuch, einen nicht konstanten Wert in einer Konstante zu benutzen). Dann nimm Lösung 1 oder Lösung 2. Siehe Konstanten.
Wo du ihn oft siehst
Abschnitt betitelt „Wo du ihn oft siehst“Wenn du von JavaScript oder Python kommst. In diesen Sprachen sieht eine verschachtelte Funktion die äußeren Variablen, also fühlt sich fn in fn natürlich an. In Rust übernimmt diese Aufgabe ein Closure. Lösung: let helper = |x| ...; statt fn helper(x) ....
self in einem verschachtelten fn in einer Methode. fn report(&self) { fn show() { println!("{}", self.n); } } ergibt ebenfalls E0434 und zeigt auf self. Lösung: Mach show zu einer Methode (fn show(&self) im impl) und ruf self.show() auf, oder nimm ein Closure.
Ein Funktionsparameter statt eines let. In fn total(prices: &[f64], discount: f64) ergibt ein verschachteltes fn apply(price: f64) -> f64 { price - discount } auch E0434: Parameter sind ebenfalls lokale Variablen. Lösung: Übergib discount an apply, oder schreib let apply = |price: f64| price - discount;.
Verwandte Fehler
Abschnitt betitelt „Verwandte Fehler“- E0401: can’t use generic parameters from outer item: dieselbe Regel für Typen – ein inneres
fnkann das äußereToderSelfnicht benutzen. - E0425: cannot find value in this scope: Den Namen gibt es gar nicht, zum Beispiel wegen eines Tippfehlers oder weil die Variable aus einer anderen Funktion stammt.
- E0373: closure may outlive the current function: Darauf kannst du stoßen, nachdem du auf ein Closure umgestiegen bist, wenn es eine Variable ausleiht und länger lebt als sie (Lösung:
move). - E0424:
selfis not available here:selfin einer Funktion benutzt, die keine Methode ist. - E0435: ein
const, das einelet-Variable benutzen will, wie in Lösung 3 gezeigt. - Alle Compilerfehler: die vollständige Liste der Fehlerseiten.
Kurzer Check
Abschnitt betitelt „Kurzer Check“Kurzer Check
1. In main steht let limit = 10; und ein verschachteltes fn check(n: i32) -> bool { n < limit }. Was passiert?
2. Welche Version kann die lokale Variable limit benutzen?
3. Der Wert kommt zur Laufzeit aus einer Benutzereingabe. Welche Lösung funktioniert NICHT?
Punkte: 0 / 3
Weiterlesen
Abschnitt betitelt „Weiterlesen“- E0434 – Rust error codes index (auf Englisch): die offizielle Erklärung, mit der Closure-Lösung und der Lösung mit
const/static. - Erfassen der Umgebung – Das Rust-Buch: wie Closures ihre Umgebung einfangen, was ein verschachteltes
fnnicht kann. - Capturing – Rust by Example (auf Englisch): Ausleihen, veränderbares Ausleihen und Verschieben in ein Closure, mit kleinen Beispielen.
- Item declarations – The Rust Reference (auf Englisch): die Regel, dass ein Element in einer Funktion deren Parameter und lokale Variablen nicht benutzen kann.
- Konstanten deklarieren – Das Rust-Buch: wie sich
constvonletunterscheidet und was ein konstanter Ausdruck enthalten darf.