ProgrammatieRust backend ontwikkelaar

Leg uit hoe het werken met onveranderlijke (immutable) en veranderlijke (mutable) datastructuren in Rust is georganiseerd, en wanneer is mutabiliteit nodig? Wat zijn de praktische beperkingen die hiermee gepaard gaan, en hoe kun je deze verstandig omzeilen?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

In Rust zijn datastructuren standaard onveranderlijk. Dit betekent dat elke variabele of verwijzing die zonder het sleutelwoord mut is gedeclareerd, niet kan worden veranderd na initialisatie.

let mut value = 10; // veranderlijke variabele value += 5; let value2 = 10; // onveranderlijke variabele // value2 += 5; // compilatiefout: cannot assign twice to immutable variable

Dezelfde logica geldt voor de velden van structuren:

struct Point { x: i32, y: i32 } let mut pt = Point { x: 1, y: 2 }; pt.x = 5; // OK, omdat pt is gedeclareerd als mut

Echter, mutabiliteit heeft alleen betrekking op het "bovenste niveau" van de variabele: als de structuur achter een onveranderlijke verwijzing is opgeslagen, kunnen de gegevens niet worden gewijzigd, zelfs als ze als mut binnen de structuur zijn gedeclareerd.

Een andere manier om beperkingen te omzeilen is door gebruik te maken van speciale typen zoals RefCell<T> of atomische containers. Dit maakt het mogelijk om gegevens binnen schijnbaar onveranderlijke containers te wijzigen via "interne mutabiliteit" (interior mutability), bijvoorbeeld:

use std::cell::RefCell; struct Counter { count: RefCell<i32>, } let counter = Counter { count: RefCell::new(0) }; *counter.count.borrow_mut() += 1; // veilig, ondanks het ontbreken van mut

Gevaarlijke vraag.

Vraag: Kan een waarde van een structs veld worden gewijzigd, als de variabele zelf niet is gedeclareerd met mut, maar het veld duidelijk is gedeclareerd als mut in de structuur zelf?

Typisch fout antwoord: Ja, als het veld met mut is gedeclareerd, kan het worden gewijzigd.

Juist antwoord: Het sleutelwoord mut kan niet worden gebruikt bij het declareren van een veld in een structuur. Mutabiliteit is slechts van toepassing op de variabele zelf of op de verkregen verwijzing. Om een veld van een structuur te wijzigen, moet je ofwel de variabele als mut dekken, of gebruik maken van typen met "interne mutabiliteit" (bijvoorbeeld RefCell).

Voorbeeld:

struct Foo { val: i32 } // veld wordt gedeclareerd zonder mut let mut foo = Foo { val: 1 }; foo.val = 2; // OK let foo2 = Foo { val: 3 }; foo2.val = 4; // fout! variabele is niet mutable

Voorbeelden van echte fouten door gebrek aan kennis over de fijnere punten van het onderwerp.


Verhaal

In een groot project probeerde een van de ontwikkelaars een veld van een structuur te wijzigen, in de veronderstelling dat het declareren van het veld met mut ("struct S { mut x: i32 }") de benodigde mutabiliteit zou geven. Dit kon niet compileren, en het refactoren duurde enkele uren totdat hem werd uitgelegd dat mutabiliteit een eigenschap is van het eigendom van de variabele, en niet van de structuur.


Verhaal

In een project dat met multithreading te maken had, was de hele logica voor toegangscontrole tot gegevens opgeslagen in een globale statische structuur. Niet wetende over de mogelijkheden van "interne mutabiliteit" via RefCell, schreef het team extreem complexe mechanismen voor vergrendeling en race, in plaats van een eenvoudige oplossing met Arc<Mutex<T>> of RwLock<T>.


Verhaal

Een ontwikkelaar vergat een functieargument als mut in de signatuur te declareren, en de functie kon de doorgegeven structuur niet wijzigen, waardoor de bug alleen in productie verscheen (gegevens werden niet bijgewerkt na de functie-aanroep). Weten dat de overdracht standaard via een onveranderlijke verwijzing gaat, zou hem in staat hebben gesteld om zonder fouten door de vroege stadia te komen.