Storia della domanda:
Rust è stato originariamente concepito come un linguaggio con la priorità sulla sicurezza della memoria. Tuttavia, in alcune attività — ad esempio, quando si lavora con FFI o allocatori a basso livello — è necessario utilizzare raw pointers e gestire la memoria dinamica manualmente. Queste attività si incontrano sia nella programmazione di sistemi, sia nell'ottimizzazione delle prestazioni. Pertanto, è importante sapere come Rust previene perdite di memoria, dangling pointers e use-after-free.
Problema:
I raw pointers (*const T, *mut T) non sono integrati nel sistema di proprietà e controllo dei riferimenti di Rust: possono puntare a memoria non valida, essere liberati in modo errato o non essere liberati affatto. Un errore nel loro uso può portare a UB (undefined behavior), crash, vulnerabilità di sicurezza o perdite di memoria.
Soluzione:
Invece di raw pointers è raccomandato utilizzare tipi sicuri — Box, Rc, Arc, e per i riferimenti temporanei — borrow-references. Se non si può fare a meno dei raw pointers (ad esempio, per lavorare con API C), tutto il lavoro viene avvolto in blocchi unsafe, gestendo con attenzione il Drop e, se possibile, utilizzando crate come NonNull. Un'altra tecnica è l'uso di wrapper RAII e la minimizzazione del ciclo di vita del puntatore.
Esempio di codice:
fn allocate_in_heap() -> Box<i32> { Box::new(100) } // la memoria sarà liberata automaticamente // con raw pointer unsafe fn leak_memory() { let ptr = libc::malloc(4) as *mut i32; if !ptr.is_null() { *ptr = 42; // libc::free(ptr); // se dimentichi di liberarlo — perdita di memoria! } }
Caratteristiche chiave:
Garantisce Box la pulizia automatica di tutti i valori annidati alla rimozione di Box?
Sì, alla rimozione di Box<T> il distruttore richiama la pulizia prima della propria confezione, poi ricorsivamente — di tutti i dati annidati all'interno (fino agli elementi di Vec o altri Box all'interno della struttura T).
È possibile passare in sicurezza un raw pointer di una struttura attraverso diverse funzioni, senza correre il rischio di un use-after-free?
No, il raw pointer non porta informazioni sulla durata di vita dell'oggetto. Il compilatore non può verificare la sicurezza, quindi la responsabilità ricade interamente sullo sviluppatore: se l'oggetto è stato liberato, il raw pointer punterà nel vuoto.
Se utilizziamo manualmente free o drop_in_place, Rust può chiamare Drop due volte per lo stesso indirizzo?
Sì, se dopo la liberazione manuale lasci un altro Box/puntatore che punta allo stesso blocco, quando il secondo oggetto viene distrutto, Drop verrà richiamato di nuovo, causando UB. Non si dovrebbe mai liberare manualmente ciò che è gestito da Box, Vec, ecc.
Un programmatore ha ricevuto un raw pointer da una libreria C esterna, non lo ha liberato dopo l'uso o perfnodo-dealloc ha sbagliato con la durata di vita.
Pro:
Contro:
Utilizza un wrapper RAII con Drop, il puntatore è incapsulato da Box o NonNull, tutto viene distrutto in modo sicuro alla fine dell'ambito.
Pro:
Contro: