ProgrammatieRust ontwikkelaar

Hoe zijn closures in Rust opgebouwd? Welke soorten closures bestaan er, wat zijn de verschillen en wanneer worden ze gebruikt?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

In Rust zijn closures anonieme functies die variabelen uit de externe scope kunnen "vangen". De syntaxis:

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

In Rust zijn er drie soorten closures (onderscheiden op basis van hoe variabelen worden gevangen):

  • Fn: vangt verwijzingen (&T), kan meerdere keren worden aangeroepen zonder de omgeving te muteren.
  • FnMut: vangt verwijzingen met de mogelijkheid tot verandering (&mut T), kan gegevens binnen de closure veranderen bij aanroep.
  • FnOnce: kan slechts één keer worden aangeroepen, omdat het eigendom van de gevangen gegevens (T) overneemt.

Rust bepaalt automatisch het benodigde type, maar je kunt het ook expliciet opgeven.

Voorbeeld van de verschillen:

let s = String::from("hello"); // FnOnce let consume = move || println!("{}", s); // s is niet meer beschikbaar na aanroep

Misleidende vraag

Waarom kan een closure met het sleutelwoord move geen FnOnce zijn, maar een Fn of FnMut?

Antwoord: Het sleutelwoord move verplaatst de gevangen variabelen op basis van eigendom, maar als de gegevens binnen de closure niet gemuteerd of vernietigd worden, blijft de closure compatibel met Fn/FnMut. Bijvoorbeeld:

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

c kan meerdere keren worden aangeroepen: de waarde s is gekopieerd in de closure, maar niet vernietigd.

Voorbeelden van echte fouten door onbekendheid met de nuances van het onderwerp


Verhaal

Een beginnende Rust-ontwikkelaar in een datastroomproject probeerde een closure zonder move te schrijven en deze door te geven aan een andere thread. Hierdoor gaf de compiler een foutmelding over outlived borrow, en moest hij snel leren over lifetime en move.


Verhaal

Introduceerde mutability binnen de closure zonder het type te veranderen naar FnMut, wat "mismatched types" veroorzaakte op alle plekken waar iterators werden gebruikt.


Verhaal

Bij het verwerken van een grote array nam de closure met move de hele collectie over, waardoor onbewust het eigendom werd verplaatst, waardoor de buitenste code geen toegang meer had tot de gegevens na de eerste iteratie, wat leidde tot pogingen om naar een moved value te verwijzen.