Salt la conținutul principal
Modulul 6 · Lecția 2 din 212/14 în curs~15 min
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:

  1. 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ă.
  2. 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ă:

Rust
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):

Rust
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ți Arc<T> (Atomic Reference Counted).

Rust
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

Exercițiu#rust.m6.l2.e1
Încercări: 0Se încarcă…

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 *.

Se încarcă editorul...
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)

Exercițiu#rust.m6.l2.e2
Încercări: 0Se încarcă…

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.

Se încarcă editorul...
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

Exercițiu#rust.m6.l2.e3
Încercări: 0Se încarcă…

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.

Se încarcă editorul...
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