ProgrammazioneSviluppatore Rust

Come sono strutturate le chiusure (closures) in Rust? Quali tipi di chiusure esistono, come si differenziano e quando vengono utilizzate?

Supera i colloqui con l'assistente IA Hintsage

Risposta

In Rust, le chiusure sono funzioni anonime che possono "catturare" variabili dall'ambiente esterno. La sintassi:

let add = |a: i32, b: i32| a + b;

In Rust ci sono tre tipi di chiusure (differiscono in base al modo in cui catturano le variabili):

  • Fn: cattura riferimenti (&T), può essere chiamata più volte senza mutare l'ambiente.
  • FnMut: cattura riferimenti con la possibilità di modificare (&mut T), i dati della chiusura possono essere modificati durante la chiamata.
  • FnOnce: può essere chiamata solo una volta, in quanto prende possesso dei dati catturati (T).

Rust determina automaticamente il tipo necessario, ma puoi specificarlo esplicitamente.

Esempio di differenze:

let s = String::from("hello"); // FnOnce let consume = move || println!("{}", s); // s non è più accessibile dopo la chiamata

Domanda trabocchetto

Perché una chiusura con la parola chiave move può non essere FnOnce, ma essere Fn o FnMut?

Risposta: La parola chiave move trasferisce la cattura delle variabili per possesso, ma se i dati all'interno della chiusura non vengono mutati e non vengono distrutti, la chiusura rimane compatibile con Fn/FnMut. Ad esempio:

let s = String::from("abc"); let c = move || println!("String: {}", s); // c: impl Fn() c();

c può essere chiamata più volte: il valore s è stato copiato nella chiusura, ma non è stato distrutto.

Esempi di errori reali a causa della mancanza di conoscenza delle sottigliezze dell'argomento


Storia

Un nuovo sviluppatore Rust in un progetto con flussi di dati ha cercato di scrivere una chiusura senza move, passandola a un altro thread. Di conseguenza, il compilatore segnalava outlived borrow e ha dovuto affrontare rapidamente le sottigliezze di lifetime e move.


Storia

Hanno introdotto la mutabilità all'interno della chiusura, senza cambiare il tipo in FnMut, il che ha causato "mismatched types" in tutti i punti in cui venivano utilizzati iterator.


Storia

Durante l'elaborazione di un grande array, la chiusura ha preso per move tutta la collezione, trasferendo inconsapevolmente il possesso, il che ha portato a perdere l'accesso ai dati dopo il primo ciclo, causando tentativi di accedere a un valore spostato.