Lecții din modul (2/2)
Indicatoare inteligente: Box și Rc
Un pointer inteligent este o structură de date care se comportă ca un indicator, dar are metadate și capabilități suplimentare (cum ar fi numărarea referințelor sau gestionarea automată a memoriei).
În Rust, referințele obișnuite (cum ar fi &) împrumută doar date. Indicatorii inteligente, pe de altă parte, dețin adesea datele către care indică. Exemplele comune includ String și Vec<T>, dar biblioteca standard oferă altele specializate.
Caseta<T> pentru alocare heap
Cel mai simplu indicator inteligent este Box<T>, care stochează date mai degrabă în heap decât în stivă. Doar indicatorul către datele alocate în heap rămâne pe stivă.
Este utilizat în principal în următoarele cazuri:
- Când aveți un tip a cărui dimensiune nu poate fi cunoscută în momentul compilării (cum ar fi un tip recursiv) și doriți să utilizați o valoare de acel tip într-un context care necesită o dimensiune exactă.
- Când doriți să transferați dreptul de proprietate asupra unei cantități mari de date fără a le copia.
Exemplu de bază:
fn main() {
let b = Box::new(5); // allocates the value 5 on the heap
println!("b = {}", *b); // dereferences with * to read the value
}Tipuri recursive și liste contra
Un tip recursiv este un tip care poate conține o valoare a lui însuși ca parte a definiției sale. Rust trebuie să cunoască dimensiunea exactă a unui tip în momentul compilării. Deoarece adâncimea unui tip recursiv poate fi infinită, compilatorul generează o eroare dacă nu utilizați o casetă (care are o dimensiune fixă, deoarece este un pointer):
enum List {
Cons(i32, Box<List>),
Nil,
}Rc<T>: Numărarea referințelor
Există cazuri în care o singură valoare poate avea mai mulți proprietari. Indicatorul inteligent Rc<T> (Reference Counted) ține evidența numărului de referințe la o valoare pentru a determina dacă valoarea este încă în uz. Dacă numărul scade la zero, resursa este dealocată în siguranță.
[!NOTĂ]
Rc<T>este potrivit doar pentru scenarii cu un singur thread. Pentru scenariile cu mai multe fire (concurente), trebuie să utilizațiArc<T>(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));
}Încercați-l singur
Exercițiul 1: Alocarea heap cu Box
Alocați o valoare întreagă de 42 pe heap folosind Box::new și atribuiți-o unei variabile numite val. Apoi, tipăriți valoarea stocată în casetă, dereferențând-o în mod explicit cu operatorul *.
Afișează indiciu
Folosiți `let val = Box::new(42);` pentru a stoca valoarea în heap și dereferiți-o cu `*val` în interiorul `println!`.
Soluție disponibilă după 3 încercări
Exercițiul 2: Structura de date recursive (Lista dezavantajelor)
Definiți o enumerare recursivă numită Listă care conține două variante: Cons, care include un tuplu (i32, Box<List>) și Nil care reprezintă sfârșitul listei. În funcția principală, instanțiați o listă legată care conține elementul 1 urmat de Nil.
Afișează indiciu
Enum este declarat ca `enum List { Cons(i32, Box<List>), Nil }`. Instanțiați lista cu `List::Cons(1, Box::new(List::Nil))`.
Soluție disponibilă după 3 încercări
Exercițiul 3: Împărtășirea proprietății cu Rc
Utilizați std::rc::Rc pentru a crea un număr întreg partajat care conține valoarea 100 într-o variabilă a. Clonează referința într-o variabilă b folosind Rc::clone. În cele din urmă, tipăriți numărul de referințe active folosind funcția Rc::strong_count.
Afișează indiciu
Creați valoarea cu `Rc::new(100)`. Clonează cu `Rc::clone(&a)` și afișează numărul de proprietari cu`Rc::strong_count(&a)`.
Soluție disponibilă după 3 încercări