Przejdź do głównej treści
eLearner.app
Moduł 6 · Lekcja 2 z 212/14 w kursie~15 min
Lekcje modułu (2/2)

Inteligentne wskaźniki (Smart Pointers): Box i Rc

Inteligentny wskaźnik (smart pointer) to struktura danych, która zachowuje się jak wskaźnik, ale posiada dodatkowe metadane i funkcjonalności (takie jak zliczanie referencji lub automatyczne zarządzanie pamięcią).

W języku Rust zwykłe referencje (takie jak &) jedynie pożyczają dane. Z kolei inteligentne wskaźniki często stają się właścicielami danych, na które wskazują. Typowymi przykładami są String oraz Vec<T>, ale biblioteka standardowa dostarcza inne, bardziej wyspecjalizowane wskaźniki.


Box<T> do alokacji pamięci na stercie (Heap)

Najprostszym inteligentnym wskaźnikiem jest Box<T>, który przechowuje dane na stercie (heap) zamiast na stosie. Na stosie pozostaje jedynie wskaźnik do danych zaalokowanych na stercie.

Jest używany głównie w następujących przypadkach:

  1. Kiedy mamy typ, którego rozmiar nie może być znany w momencie kompilacji (jak typ rekurencyjny), a chcemy użyć wartości tego typu w kontekście wymagającym dokładnego rozmiaru.
  2. Kiedy chcemy przenieść duże ilości danych bez ich kopiowania.

Podstawowy przykład:

Code
fn main() {
    let b = Box::new(5); // alloca il valore 5 nello heap
    println!("b = {}", *b); // de-referenzia con * per leggere il valore
}

Typy rekurencyjne i lista Cons (Cons List)

Typ rekurencyjny to typ, który może zawierać wartość samego siebie w swoim wnętrzu. Rust musi znać dokładny rozmiar typu w czasie kompilacji. Ponieważ głębokość typu rekurencyjnego mogłaby być nieskończona, kompilator wygeneruje błąd, chyba że użyjemy Box (który ma stały rozmiar, ponieważ jest wskaźnikiem):

Code
enum List {
    Cons(i32, Box<List>),
    Nil,
}

Rc<T>: Zliczanie referencji (Reference Counting)

Istnieją przypadki, w których pojedyncza wartość może mieć wielu właścicieli. Inteligentny wskaźnik Rc<T> (Reference Counted) śledzi liczbę referencji do wartości, aby ustalić, czy wartość jest nadal używana. Jeśli licznik spadnie do zera, zasób jest bezpiecznie usuwany z pamięci.

[!NOTE] Rc<T> może być używany wyłącznie w scenariuszach jednowątkowych (single-thread). W scenariuszach wielowątkowych (współbieżnych) należy stosować Arc<T> (Atomic Reference Counted).

Code
use std::rc::Rc;

fn main() {
    let a = Rc::new(5);
    let b = Rc::clone(&a); // incrementa il contatore a 2
    println!("Riferimenti: {}", Rc::strong_count(&a));
}

Spróbuj sam

Esercizio 1: Alokowanie za pomocą Box

Ćwiczenie#rust.m6.l2.e1
Próby: 0Ładowanie...

Zaalokuj wartość całkowitą równą 42 na stercie (heap) za pomocą Box::new i przypisz ją do zmiennej o nazwie val. Następnie wypisz na ekranie wartość zapisaną w Box, jawnie ją dereferencjonując za pomocą operatora *.

Ładowanie edytora...
Pokaż wskazówkę

Użyj `let val = Box::new(42);` do zapisania wartości na stercie i dereferencjonuj ją za pomocą `*val` wewnątrz `println!`.

Rozwiązanie dostępne po 3 próbach

Esercizio 2: Rekurencyjna struktura danych (Cons List)

Ćwiczenie#rust.m6.l2.e2
Próby: 0Ładowanie...

Zdefiniuj rekurencyjny enum o nazwie List zawierający dwa warianty: Cons, który zawiera krotkę (i32, Box<List>), oraz Nil reprezentujący koniec listy. W funkcji main utwórz instancję listy wiązanej zawierającej element 1, po którym następuje Nil.

Ładowanie edytora...
Pokaż wskazówkę

Enum deklaruje się jako `enum List { Cons(i32, Box<List>), Nil }`. Utwórz listę za pomocą `List::Cons(1, Box::new(List::Nil))`.

Rozwiązanie dostępne po 3 próbach

Esercizio 3: Dzielenie własności z Rc

Ćwiczenie#rust.m6.l2.e3
Próby: 0Ładowanie...

Użyj std::rc::Rc do utworzenia współdzielonej liczby całkowitej zawierającej wartość 100 w zmiennej a. Sklonuj referencję w zmiennej b przy użyciu Rc::clone. Na koniec wypisz na ekranie licznik aktywnych referencji przy użyciu funkcji Rc::strong_count.

Ładowanie edytora...
Pokaż wskazówkę

Utwórz wartość za pomocą `Rc::new(100)`. Sklonuj ją za pomocą `Rc::clone(&a)`i wyświetl liczbę właścicieli za pomocą`Rc::strong_count(&a)`.

Rozwiązanie dostępne po 3 próbach