Errore Rust E0434: can't capture dynamic environment in a fn item (variabile locale)
E0434 significa che una funzione scritta dentro un’altra funzione ha provato a usare una delle variabili locali della funzione esterna. Sembra che dovrebbe funzionare: la variabile è proprio lì, qualche riga più su. Ma in Rust una fn annidata è un item separato, e non vede le variabili che ha intorno. La formulazione del compilatore, «dynamic environment» (ambiente dinamico), significa solo «le variabili locali che esistono mentre la funzione esterna è in esecuzione». È uno degli errori più facili da correggere: rustc ti dà la risposta nella sua riga help:. (Questa pagina fa parte di Errori del compilatore Rust spiegati.)
Il messaggio di errore
Sezione intitolata “Il messaggio di errore”error[E0434]: can't capture dynamic environment in a fn item --> src/main.rs:5:24 |5 | price * (1.0 + tax_rate) // ❌ inner `fn` can't see `tax_rate` | ^^^^^^^^ | = help: use the `|| { ... }` closure form insteadIl compilatore dice: un item fn non può catturare l’ambiente dinamico, e ti suggerisce di usare una closure. Questo è il programma che produce l’errore:
fn main() { let tax_rate = 0.2;
fn with_tax(price: f64) -> f64 { price * (1.0 + tax_rate) // ❌ inner `fn` can't see `tax_rate` }
println!("{:.2}", with_tax(10.0));}Cosa significa
Sezione intitolata “Cosa significa”Il ^^^ indica tax_rate alla riga 5. È una variabile locale di main, creata con let alla riga 2. La funzione interna with_tax cerca di leggerla, e non è permesso.
La riga help: ti dà la correzione: scrivi una closure, || { ... }, al posto di una fn annidata. Una closure può usare le variabili che ha intorno. Una fn no, in qualunque punto tu la scriva.
Spesso ricevi anche un warning, unused variable: `tax_rate` (variabile non usata), perché dal punto di vista di Rust niente potrebbe usarla in modo lecito. Quel warning sparisce insieme all’errore.
È come una lettera sigillata che hai scritto alla tua scrivania. Non conta che i tuoi appunti fossero sulla scrivania mentre la scrivevi; chi la apre vede solo ciò che hai messo nella busta.
Perché Rust si comporta così
Sezione intitolata “Perché Rust si comporta così”Una fn annidata viene compilata esattamente come una in cima al file. Scriverla dentro main nasconde solo il suo nome al resto del codice. Non la collega alle variabili di main, e c’è un buon motivo: una funzione normale non ha un posto dove tenerle. Puoi chiamare with_tax da qualsiasi punto in cui main la raggiunge, salvarla come puntatore a funzione, o chiamarla quando le variabili di main non esistono più. Una fn normale è solo codice, senza dati attaccati.
Una closure è codice più le variabili che ha catturato. Rust conserva quei valori (o riferimenti a quei valori) dentro la closure, e il borrow checker si assicura che vivano abbastanza a lungo. Ecco perché rustc ti indirizza alle closure. Vedi Closure in Rust.
La stessa regola vale per i parametri generici: neanche il T della funzione esterna si può usare in una fn interna. Quello è E0401.
Come risolverlo
Sezione intitolata “Come risolverlo”Soluzione 1: usa una closure
Sezione intitolata “Soluzione 1: usa una closure”Scegli questa nella maggior parte dei casi, quando l’helper è piccolo e vive dentro una sola funzione. Scrivi let nome = |parametri| corpo; e la closure può usare qualsiasi variabile in scope:
fn main() { let tax_rate = 0.2;
let with_tax = |price: f64| price * (1.0 + tax_rate); // ✅ una closure vede `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!("totale: {:.2}", total);}Output:
12.0029.40totale: 27.00Una closure che si limita a leggere una variabile la prende in prestito, quindi dopo puoi ancora usare tax_rate. Se modifichi la variabile dentro la closure, la closure ha bisogno di let mut. Vedi Closure in Rust.
Soluzione 2: passa il valore come argomento
Sezione intitolata “Soluzione 2: passa il valore come argomento”Scegli questa quando l’helper deve restare una vera funzione: è più lungo, vuoi testarlo, o lo usano più funzioni. Dagli un parametro in più e passagli il valore:
fn with_tax(price: f64, tax_rate: f64) -> f64 { price * (1.0 + tax_rate) // ✅ ora `tax_rate` è un parametro}
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!("totale: {:.2}", receipt(&[3.0, 7.5, 12.0], tax_rate));
let reduced_rate = 0.05; println!("{:.2}", with_tax(10.0, reduced_rate));}Output:
12.00totale: 27.0010.50Qui la funzione è stata spostata al livello più esterno, dove vive la maggior parte degli helper in Rust. Funziona anche se la lasci annidata; ciò che conta è che il valore arrivi come argomento. In più: ora funziona con qualsiasi aliquota, non solo con una. Vedi Funzioni in Rust.
Soluzione 3: rendilo una const
Sezione intitolata “Soluzione 3: rendilo una const”Scegli questa quando il valore è fisso e noto prima che il programma parta, come un’aliquota scritta nel codice. A differenza di una variabile let, una const è un item, e qualsiasi funzione può usarla, annidata o no:
const TAX_RATE: f64 = 0.2; // ✅ una costante, visibile a ogni funzione
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));}Output:
12.0029.40Funziona solo per valori che si possono calcolare in fase di compilazione. Se l’aliquota arriva da un file, dall’input dell’utente o da un’altra variabile, una const non va bene: const RATE: f64 = tax_rate; dà un errore diverso, E0435 (attempt to use a non-constant value in a constant). In quel caso usa la soluzione 1 o la 2. Vedi Variabili in Rust.
Dove lo incontri di solito
Sezione intitolata “Dove lo incontri di solito”Se arrivi da JavaScript o Python. In quei linguaggi una funzione annidata vede le variabili esterne, quindi una fn dentro una fn sembra naturale. In Rust la cosa che fa questo lavoro è una closure. Correzione: let helper = |x| ...; al posto di fn helper(x) ....
Usare self in una fn annidata dentro un metodo. fn report(&self) { fn show() { println!("{}", self.n); } } dà lo stesso E0434, indicando self. Correzione: rendi show un metodo (fn show(&self) nell’impl) e chiama self.show(), oppure usa una closure.
Un parametro di funzione, non un let. In fn total(prices: &[f64], discount: f64), una fn apply(price: f64) -> f64 { price - discount } annidata dà anche lei E0434: anche i parametri sono variabili locali. Correzione: passa discount ad apply, oppure scrivi let apply = |price: f64| price - discount;.
Errori correlati
Sezione intitolata “Errori correlati”- E0401: can’t use generic parameters from outer item: la stessa regola per i tipi: una
fninterna non può usare ilTo ilSelfesterno. - E0425: cannot find value in this scope: il nome non esiste proprio, per esempio per un errore di battitura o una variabile di un’altra funzione.
- E0373: closure may outlive the current function: quello in cui puoi imbatterti dopo essere passato a una closure, se prende in prestito una variabile e vive più a lungo di lei (correzione:
move). - E0424:
selfis not available here:selfusato in una funzione che non è un metodo. - E0435: una
constche cerca di usare una variabilelet, vedi la soluzione 3. - Tutti gli errori del compilatore spiegati
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Dentro main hai let limit = 10; e una fn check(n: i32) -> bool { n < limit } annidata. Che cosa succede?
2. Quale versione può usare la variabile locale limit?
3. Il valore arriva dall'input dell'utente durante l'esecuzione. Quale correzione NON funziona?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- E0434 — Rust error codes index (in inglese): la spiegazione ufficiale, con la correzione tramite closure e quella con
const/static. - Closures — The Rust Book (in inglese): come le closure catturano il loro ambiente, cosa che una
fnannidata non sa fare. - Capturing — Rust by Example (in inglese): prestito, prestito mutabile e spostamento dentro una closure, con piccoli esempi.
- Item declarations — The Rust Reference (in inglese): la regola per cui un item dentro una funzione non può usare i parametri o le variabili locali della funzione.
- Constants — The Rust Book (in inglese): in che cosa
constè diversa dalet, e che cosa può contenere un’espressione costante.