ProgrammatieBackend ontwikkelaar

Hoe werken slimme pointers in Rust (Box, Rc, Arc, RefCell)? Wat zijn de verschillen tussen hen en in welke gevallen moet je welke kiezen?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

In Rust is er geen klassieke garbage collector, daarom worden slimme pointers gebruikt voor het beheren van eigendom van complexe structuren. De meest gebruikte zijn:

  • Box<T> — allocates geheugen voor een object op de heap en overdraagt het eigendom ervan. Wordt gebruikt voor gevallen waarin de grootte van de gegevens niet bekend is tijdens de compilatie of wanneer een verplaatsbare, maar unieke resource vereist is.

  • Rc<T> (Reference Counted) — referentietelling, stelt meerdere variabelen in staat om het eigendom van onveranderlijke gegevens te "delen" (alleen in een één-thread context).

  • Arc<T> (Atomic Reference Counted) — implementeert ook referentietelling, maar atomair; gebruik is toegestaan in multi-thread programma's.

  • RefCell<T> — biedt "interne mutabele" eigendom op runtime, waardoor inhoud zelfs via een onveranderlijke referentie kan worden gewijzigd, maar alleen in één thread (niet thread-veilig!).

Voorbeeld:

use std::rc::Rc; let a = Rc::new(vec![1,2,3]); let b = Rc::clone(&a); // Nu zijn zowel a als b eigenaren van dezelfde gegevens

Vrag met een addertje onder het gras

Kan Rc<T> worden gebruikt in multi-thread code, als alle threads alleen de gegevens lezen? Leg uit.

Antwoord: Nee, dat kan niet! Ondanks dat Rc<T> alleen onveranderlijke toegang tot de gegevens toestaat, is de Rc<T> container zelf niet thread-veilig, omdat het interne referentietel niet beschermd is tegen race conditions. Hiervoor is Arc<T> bedoeld — de interne teller is thread-veilig.

Voorbeeld:

// De volgende code compileert niet! use std::thread; use std::rc::Rc; let five = Rc::new(5); for _ in 0..10 { let five = Rc::clone(&five); thread::spawn(move || { println!("{}", five); }); }

Voorbeelden van echte fouten door onwetendheid over de details van het onderwerp


Verhaal

Pogingen om Rc<T> te gebruiken voor het delen van een cache tussen threads om een webservice te versnellen. In productie kregen we vreemde crashes en beschadigde gegevens. Na onderzoek bleek dat Rc niet thread-veilig is, en de referentieteller was beschadigd. Oplossing: vervanging door Arc<T>.

Verhaal

In een desktopapplicatie werd een grote boomstructuur van objecten opgeslagen in Box<T>, maar men had niet in overweging genomen dat verschillende delen van de UI eigendom van de gegevens moesten delen. Dit leidde tot compilatiefouten. De oplossing was het gebruik van Rc<T> voor het delen van toegang.

Verhaal

In de business logic module werd RefCell<T> gebruikt voor het organiseren van mutabele toegang tot gegevens, die ook via Arc<T> tussen threads werden doorgegeven. Maar het proberen om RefCell<T> en Arc<T> te combineren leidde tot races en paniek tijdens runtime. Voor een thread-veilige variant had men Mutex<T> of RwLock<T> in plaats van RefCell<T> moeten gebruiken.