Salta ai contenuti

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

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 instead

Il 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));
}

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.

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.

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.00
29.40
totale: 27.00
▶ Provalo tu

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

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.00
totale: 27.00
10.50
▶ Provalo tu

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

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.00
29.40
▶ Provalo tu

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

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

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