Modulelessen (2/2)
Slimme wijzers: Box en Rc
Een slimme pointer is een datastructuur die zich gedraagt als een pointer, maar beschikt over metagegevens en extra mogelijkheden (zoals het tellen van referenties of automatisch geheugenbeheer).
In Rust lenen gewone referenties (zoals &) alleen gegevens. Slimme wijzers daarentegen zijn vaak eigenaar van de gegevens waarnaar ze verwijzen. Veel voorkomende voorbeelden zijn String en Vec<T>, maar de standaardbibliotheek biedt andere gespecialiseerde bibliotheek.
Box<T> voor heaptoewijzing
De eenvoudigste slimme aanwijzer is Box<T>, waarmee gegevens op de heap worden opgeslagen in plaats van op de stapel. Alleen de pointer naar de aan de heap toegewezen gegevens blijft op de stapel staan.
Het wordt voornamelijk gebruikt in de volgende gevallen:
- Wanneer u een type heeft waarvan de grootte niet bekend is tijdens het compileren (zoals een recursief type), en u een waarde van dat type wilt gebruiken in een context die een exacte grootte vereist.
- Wanneer u het eigendom van een grote hoeveelheid gegevens wilt overdragen zonder deze te kopiëren.
Basisvoorbeeld:
fn main() {
let b = Box::new(5); // allocates the value 5 on the heap
println!("b = {}", *b); // dereferences with * to read the value
}Recursieve typen en nadelenlijsten
Een recursief type is een type dat een waarde van zichzelf kan bevatten als onderdeel van de definitie ervan. Rust moet tijdens het compileren de exacte grootte van een type weten. Omdat de diepte van een recursief type oneindig kan zijn, genereert de compiler een fout, tenzij je een Box gebruikt (die een vaste grootte heeft, omdat het een pointer is):
enum List {
Cons(i32, Box<List>),
Nil,
}Rc<T>: Referentietelling
Er zijn gevallen waarin een enkele waarde meerdere eigenaren kan hebben. De slimme aanwijzer Rc<T> (Reference Counted) houdt het aantal verwijzingen naar een waarde bij om te bepalen of de waarde nog in gebruik is. Als het aantal naar nul daalt, wordt de toewijzing van de bron veilig opgeheven.
[!NOTE]
Rc<T>is alleen geschikt voor scenario's met één thread. Voor scenario's met meerdere threads (gelijktijdig) moet uArc<T>gebruiken (Atomic Reference Counted).
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = Rc::clone(&a); // increments the reference count to 2
println!("References: {}", Rc::strong_count(&a));
}Probeer het zelf
Oefening 1: Heaptoewijzing met Box
Wijs een geheel getal van 42 toe aan de heap met behulp van Box::new en wijs deze toe aan een variabele met de naam val. Druk vervolgens de waarde af die is opgeslagen in de Box door deze expliciet te derefereren met de operator *.
Toon hint
Gebruik `let val = Box::new(42);` om de waarde op te slaan in heap en verwijder de referentie met `*val` binnen `println!`.
Oplossing beschikbaar na 3 pogingen
Oefening 2: Recursieve gegevensstructuur (lijst met nadelen)
Definieer een recursieve opsomming met de naam Lijst die twee varianten bevat: Cons die een tupel omsluit (i32, Box<Lijst>) en Nil die het einde van de lijst vertegenwoordigt. Maak in de hoofdfunctie een gekoppelde lijst met het element 1 gevolgd door Nil.
Toon hint
De enum wordt gedeclareerd als `enum List { Cons(i32, Box<List>), Nil }`. Instantieer de lijst met `List::Cons(1, Box::new(List::Nil))`.
Oplossing beschikbaar na 3 pogingen
Oefening 3: Eigendom delen met Rc
Gebruik std::rc::Rc om een gedeeld geheel getal te maken dat de waarde 100 in een variabele a bevat. Kloon de verwijzing naar een variabele b met behulp van Rc::clone. Druk ten slotte het aantal actieve referenties af met behulp van de functie Rc::strong_count.
Toon hint
Creëer de waarde met `Rc::new(100)`. Kloon met `Rc::clone(&a)`en geef het aantal eigenaren weer met`Rc::strong_count(&a)`.
Oplossing beschikbaar na 3 pogingen