00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Smart Pointers in Rust

Gli smart pointers in Rust sono strutture dati che si comportano come puntatori ma offrono funzionalitĂ  aggiuntive come il conteggio dei riferimenti e la mutabilitĂ  interna.

Box<T> - Allocazione sullo Heap

Box<T> alloca un valore sullo heap, utile per tipi di dimensione sconosciuta a compile-time:

fn main() {
    let b = Box::new(5);
    println!("{}", b); // 5 - si usa come un riferimento normale

    // Utile per tipi ricorsivi
    enum Lista {
        Elemento(i32, Box<Lista>),
        Fine,
    }

    let lista = Lista::Elemento(1,
        Box::new(Lista::Elemento(2,
            Box::new(Lista::Fine))));
}

Rc<T> - Reference Counting

Rc<T> permette ownership condivisa tramite conteggio dei riferimenti (single-threaded):

use std::rc::Rc;

fn main() {
    let dati = Rc::new(vec![1, 2, 3]);

    let clone1 = Rc::clone(&dati); // Incrementa il contatore, non copia i dati
    let clone2 = Rc::clone(&dati);

    println!("Riferimenti: {}", Rc::strong_count(&dati)); // 3
    println!("{:?}", clone1); // [1, 2, 3]
}
// Tutti i Rc vengono deallocati quando il contatore arriva a 0

Arc<T> - Atomic Reference Counting

Arc<T> è come Rc<T> ma thread-safe:

use std::sync::Arc;
use std::thread;

fn main() {
    let dati = Arc::new(vec![1, 2, 3, 4, 5]);
    let mut handles = vec![];

    for i in 0..3 {
        let dati_clone = Arc::clone(&dati);
        let handle = thread::spawn(move || {
            println!("Thread {}: {:?}", i, dati_clone);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

RefCell<T> - MutabilitĂ  Interna

RefCell<T> sposta il controllo del borrowing da compile-time a runtime:

use std::cell::RefCell;

fn main() {
    let dati = RefCell::new(vec![1, 2, 3]);

    // Borrow immutabile
    println!("{:?}", dati.borrow());

    // Borrow mutabile
    dati.borrow_mut().push(4);

    println!("{:?}", dati.borrow()); // [1, 2, 3, 4]

    // PANIC a runtime se si viola la regola dei borrow:
    // let r1 = dati.borrow();
    // let r2 = dati.borrow_mut(); // PANIC!
}

Pattern Comune: Rc + RefCell

Combinazione per ownership condivisa con mutabilitĂ :

use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
struct Nodo {
    valore: i32,
    figli: Vec<Rc<RefCell<Nodo>>>,
}

fn main() {
    let foglia = Rc::new(RefCell::new(Nodo {
        valore: 1,
        figli: vec![],
    }));

    let radice = Rc::new(RefCell::new(Nodo {
        valore: 0,
        figli: vec![Rc::clone(&foglia)],
    }));

    // Modifica la foglia tramite RefCell
    foglia.borrow_mut().valore = 10;
}

Weak<T> - Riferimenti Deboli

Weak<T> previene i cicli di riferimento con Rc:

use std::rc::{Rc, Weak};
use std::cell::RefCell;

struct Nodo {
    valore: i32,
    padre: RefCell<Weak<Nodo>>,
    figli: RefCell<Vec<Rc<Nodo>>>,
}

Deref e Drop

Gli smart pointers implementano Deref (per usarli come riferimenti) e Drop (per cleanup automatico):

use std::ops::Deref;

struct MioBox<T>(T);

impl<T> Deref for MioBox<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.0
    }
}

Conclusione

Gli smart pointers estendono il sistema di ownership di Rust per gestire pattern complessi: Box per l’allocazione heap, Rc/Arc per l’ownership condivisa, RefCell per la mutabilità interna. Scegli Rc per codice single-threaded e Arc per codice multi-threaded. Usa Weak per prevenire cicli di riferimento.