# Rust smart pointer spiegati: Box, Rc, Arc e RefCell nel 2026 > Gli smart pointer di Rust Box, Rc, Arc e RefCell spiegati con esempi compilabili del 2026, una tabella decisionale e le domande da colloquio più comuni. - Published: 2026-07-05 - Updated: 2026-07-07 - Author: SharpSkill - Tags: rust, smart-pointers, memory-management, interview, rust-2024-edition - Reading time: 11 min --- Gli smart pointer di Rust sono gli strumenti che sbloccano pattern di ownership che il borrow checker da solo non riesce a esprimere: allocazione sullo heap, possesso condiviso e mutazione dietro un riferimento condiviso. Dove un semplice riferimento (`&T`) si limita a prendere in prestito un valore, uno smart pointer possiede i propri dati e vi sovrappone un comportamento aggiuntivo. Questa guida analizza i quattro tipi che ogni sviluppatore Rust incontra in produzione e nei colloqui: `Box`, `Rc`, `Arc` e `RefCell`, con esempi compilabili per la edition Rust 2024. > **Il riassunto in una riga** > > `Box` serve per l'allocazione sullo heap con un solo proprietario, `Rc` per il possesso condiviso su un singolo thread, `Arc` per il possesso condiviso tra più thread e `RefCell` per mutare un valore attraverso un riferimento condiviso. Le coppie `Rc>` e `Arc>` coprono lo stato mutabile condiviso. ## Cosa sono gli smart pointer in Rust Uno smart pointer è una struct che si comporta come un puntatore ma porta con sé metadati o capacità aggiuntive. La maggior parte implementa il trait `Deref`, così `*pointer` e le chiamate ai metodi funzionano come se il puntatore fosse un semplice riferimento, e il trait `Drop`, così la pulizia avviene automaticamente quando il valore esce dallo scope. La libreria standard fornisce i quattro tipi trattati qui, e comprenderli dipende da una solida padronanza di [ownership e borrowing](/blog/rust/ownership-borrowing-rust-complete-guide), che decide chi libera ogni allocazione e quando. La distinzione fondamentale rispetto a un riferimento è il possesso. `&T` non possiede mai i dati a cui punta, quindi non può sopravvivere al valore. Uno smart pointer possiede i dati, ne controlla la durata e li libera in modo deterministico. Il [capitolo del Rust Book sugli smart pointer](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html) li tratta come una categoria proprio perché condividono questa forma fatta di possesso più comportamento. ## `Box`: allocazione sullo heap per tipi ricorsivi e dimensionati `Box` è lo smart pointer più semplice. Memorizza un valore sullo heap e ne mantiene un puntatore sullo stack, con un unico proprietario e senza overhead a runtime oltre l'allocazione stessa. Il suo compito più comune è dare una dimensione nota ai tipi ricorsivi: un tipo che contiene sé stesso in modo diretto sarebbe infinitamente grande, ma una `Box` è solo un puntatore, quindi la sua dimensione è fissa a prescindere da ciò a cui punta. L'esempio seguente definisce un albero binario. Ogni `Node` contiene due figli e, senza indirezione, il compilatore non può calcolare la dimensione di `Tree`. Racchiudere ogni figlio in una `Box` spezza la ricorsione a livello di tipo. ```rust // tree.rs #[derive(Debug)] enum Tree { Leaf(i32), // Box puts each child on the heap, so Node has a fixed size (two pointers). Node(Box, Box), } fn sum(tree: &Tree) -> i32 { match tree { Tree::Leaf(value) => *value, // base case: return the leaf Tree::Node(left, right) => sum(left) + sum(right), // recurse into both children } } fn main() { // Build (1) + ((2) + (3)) = 6 let tree = Tree::Node( Box::new(Tree::Leaf(1)), Box::new(Tree::Node( Box::new(Tree::Leaf(2)), Box::new(Tree::Leaf(3)), )), ); println!("sum = {}", sum(&tree)); // sum = 6 } ``` `Box` conta anche quando spostare un valore grande sarebbe costoso, o quando si restituisce un trait object come `Box`. In ogni caso vale la stessa regola: un solo proprietario, liberato automaticamente quando la `Box` viene rilasciata. ## `Rc`: possesso condiviso nel codice a singolo thread A volte un valore ha bisogno di più proprietari, nessuno dei quali è chiaramente l'ultimo a usarlo. `Rc` (reference counted) risolve il problema mantenendo un conteggio forte di quanti proprietari esistono. `Rc::clone` incrementa quel conteggio senza copiare i dati sottostanti, e ogni rilascio lo decrementa. Quando il conteggio raggiunge zero, il valore viene liberato. Si consideri un oggetto di configurazione letto da più worker. Ogni worker dovrebbe mantenere la configurazione finché ne ha bisogno, e l'allocazione dovrebbe sparire solo quando l'ultimo worker se ne va. ```rust // shared_config.rs use std::rc::Rc; #[derive(Debug)] struct Config { endpoint: String, timeout_ms: u32, } fn main() { // Rc::new moves Config onto the heap with a strong count of 1. let config = Rc::new(Config { endpoint: "https://api.example.com".to_string(), timeout_ms: 5000, }); // Rc::clone only bumps the reference count; it does not deep-copy Config. let worker_a = Rc::clone(&config); let worker_b = Rc::clone(&config); // All three handles point to the same allocation. println!("endpoint: {}", worker_a.endpoint); println!("timeout: {}", worker_b.timeout_ms); // strong_count reports how many owners currently hold the value. println!("owners: {}", Rc::strong_count(&config)); // owners: 3 } ``` Chiamare `Rc::clone` in modo esplicito (invece di `config.clone()`) è idiomatico: segnala a chi legge che l'operazione è economica e tocca solo un contatore. Il rovescio della medaglia è che `Rc` distribuisce soltanto riferimenti condivisi e immutabili. Non può mutare il valore che condivide e non è sicuro inviarlo tra i thread. La [documentazione di `Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) chiarisce entrambi i vincoli. ## `RefCell` e interior mutability in Rust Rust normalmente impone che un valore sia condiviso tramite molti riferimenti immutabili oppure mutato tramite esattamente un riferimento mutabile, e lo verifica a tempo di compilazione. `RefCell` offre la interior mutability: permette al codice di mutare un valore attraverso un riferimento condiviso spostando quella stessa verifica al runtime. `borrow()` restituisce una guardia di lettura condivisa; `borrow_mut()` restituisce una guardia di scrittura esclusiva. Le regole sono identiche, ma le violazioni causano un panic invece di far fallire la compilazione. Combinato con `Rc`, questo produce il pattern canonico a singolo thread per lo stato mutabile condiviso, `Rc>`: più proprietari che possono tutti aggiornare lo stesso valore. ```rust // counter.rs use std::rc::Rc; use std::cell::RefCell; // Rc gives shared ownership; RefCell allows mutation through a shared reference. type SharedCounter = Rc>; fn increment(counter: &SharedCounter) { // borrow_mut() hands out an exclusive reference, checked at runtime. *counter.borrow_mut() += 1; } fn main() { let counter: SharedCounter = Rc::new(RefCell::new(0)); let handle_a = Rc::clone(&counter); let handle_b = Rc::clone(&counter); increment(&handle_a); increment(&handle_b); increment(&counter); // borrow() gives a shared read guard; the value is now 3. println!("count = {}", counter.borrow()); // count = 3 } ``` > **RefCell sposta il controllo dei prestiti al runtime** > > Chiamare `borrow_mut()` mentre un'altra guardia `borrow()` o `borrow_mut()` è ancora attiva causa un panic con `already borrowed: BorrowMutError`. Le garanzie di sicurezza reggono, ma un errore logico che il compilatore avrebbe intercettato diventa un crash a runtime. Conviene mantenere le guardie di breve durata ed evitare di trattenere un prestito attraverso una chiamata di funzione che potrebbe rientrare nella stessa `RefCell`. La interior mutability è anche il punto in cui si nascondono i cicli di riferimento. Due valori `Rc>` che puntano l'uno all'altro tengono per sempre il conteggio forte dell'altro sopra lo zero, quindi nessuno dei due viene mai liberato. La soluzione è `Weak`, un handle non proprietario che non incide sul conteggio forte; la spiegazione classica si trova in [Learn Rust With Entirely Too Many Linked Lists](https://rust-unofficial.github.io/too-many-lists/). ## `Arc`: reference counting thread-safe per la concorrenza `Arc` (atomically reference counted) è il fratello multi-thread di `Rc`. L'API pubblica è quasi identica, ma il contatore usa operazioni atomiche, così clonare e rilasciare un `Arc` da più thread contemporaneamente resta corretto. Questa atomicità è il motivo per cui `Arc` si sceglie solo quando la condivisione attraversa il confine di un thread: l'incremento atomico è misurabilmente più lento di quello semplice usato da `Rc`. Poiché `Arc` distribuisce comunque riferimenti immutabili, la mutazione tra thread richiede una primitiva di sincronizzazione. `Mutex` è il partner abituale e dà il pattern `Arc>`, che rispecchia `Rc>` per il codice concorrente. L'esempio avvia quattro thread che incrementano ciascuno mille volte un totale condiviso. ```rust // parallel_sum.rs use std::sync::{Arc, Mutex}; use std::thread; fn main() { // Arc is safe to share across threads; Mutex serializes access to the inner value. let total = Arc::new(Mutex::new(0u64)); let mut handles = Vec::new(); for _ in 0..4 { // Each thread gets its own Arc handle (an atomic count bump). let total = Arc::clone(&total); handles.push(thread::spawn(move || { for _ in 0..1000 { // lock() blocks until the mutex is free, then returns a guard. let mut value = total.lock().unwrap(); *value += 1; } })); } for handle in handles { handle.join().unwrap(); // wait for every thread before reading } println!("total = {}", *total.lock().unwrap()); // total = 4000 } ``` Il compilatore impone il confine: `Rc` non è `Send`, quindi provare a spostarlo dentro `thread::spawn` non compila, spingendo verso `Arc`. Ricorrere ad `Arc` all'interno di un singolo thread paga soltanto per operazioni atomiche mai usate. La [documentazione di `Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) tratta in dettaglio le garanzie di ordinamento della memoria, e `Arc>` sostiene gran parte dello stato condiviso in [Rust asincrono con Tokio](/blog/rust/rust-async-await-tokio-futures-concurrency). ## Box, Rc, Arc e RefCell a confronto: quando usare ciascuno I quattro tipi si compongono lungo due assi: quanti proprietari ha un valore e se può essere mutato tramite un handle condiviso. La tabella riassume i compromessi. | Tipo | Possesso | Mutazione tramite handle condiviso | Thread-safe | Overhead | |------|-----------|----------------------------|-------------|----------| | `Box` | Singolo | No | Sì, se `T: Send` | Solo allocazione sullo heap | | `Rc` | Condiviso | No | No | Conteggio non atomico | | `Arc` | Condiviso | No | Sì | Conteggio atomico | | `RefCell` | Singolo | Sì (verificata a runtime) | No | Flag di prestito a runtime | L'albero decisionale pratico è breve. Serve un solo proprietario sullo heap: `Box`. Servono più proprietari su un singolo thread: `Rc`. Servono più proprietari tra più thread: `Arc`. Serve mutare un valore condiviso: racchiudere il tipo interno in `RefCell` (singolo thread) o `Mutex` (multi-thread). Le indicazioni di Effective Rust sui [tipi riferimento e puntatore](https://www.lurklurk.org/effective-rust/) arrivano alla stessa stratificazione. Si parte dall'opzione meno potente e si aggiunge capacità solo quando il compilatore lo impone. ## Rust smart pointer: domande da colloquio Gli smart pointer sono un argomento affidabile nei colloqui perché verificano se un candidato comprende l'ownership e non solo la sintassi. Le domande seguenti ricorrono spesso, e altre sono raccolte nel [modulo di colloquio sugli smart pointer di Rust](/technologies/rust/interview-questions/smart-pointers). **Qual è la differenza tra `Rc` e `Arc`?** Entrambi offrono possesso condiviso tramite reference counting. `Rc` usa un contatore non atomico ed è confinato a un singolo thread; `Arc` usa operazioni atomiche e può essere condiviso tra più thread a un piccolo costo a runtime. Il compilatore impone la separazione: `Rc` non è né `Send` né `Sync`, quindi non può attraversare il confine di un thread. **Perché combinare `Rc` con `RefCell`?** `Rc` concede possesso condiviso ma solo accesso immutabile. `RefCell` aggiunge la interior mutability, permettendo ai proprietari di mutare il valore attraverso un riferimento condiviso con controlli sui prestiti a runtime. Insieme, `Rc>` è il blocco costitutivo idiomatico per lo stato mutabile condiviso a singolo thread. **Come può il reference counting causare memory leak in Rust?** Due valori `Rc` (o `Arc`) che si riferiscono a vicenda formano un ciclo i cui conteggi forti non raggiungono mai zero, quindi l'allocazione non viene mai liberata. Spezzare il ciclo con `Weak`, che mantiene un riferimento non proprietario, ripristina la pulizia corretta. **Quando è preferibile `Box` a `Rc`?** Ogni volta che basta un solo proprietario. `Box` non ha overhead di reference counting, quindi è la scelta predefinita per l'allocazione sullo heap, i tipi ricorsivi e i trait object. Si ricorre a `Rc` solo quando serve un vero possesso condiviso. ## Conclusione Gli smart pointer di Rust trasformano l'ownership da vincolo a insieme di blocchi componibili. La scelta tra loro discende direttamente dalla forma del problema: - Usare `Box` quando un valore ha bisogno di un unico proprietario sullo heap, un tipo ricorsivo ha bisogno di una dimensione fissa o una funzione restituisce un trait object. - Usare `Rc` per più proprietari su un singolo thread e passare ad `Arc` nel momento in cui il possesso attraversa il confine di un thread. - Aggiungere `RefCell` per la interior mutability nel codice a singolo thread e `Mutex` per l'equivalente concorrente, mantenendo brevi le guardie di prestito e di lock. - Combinarli con criterio: `Rc>` per lo stato mutabile condiviso su un singolo thread, `Arc>` tra più thread. - Fare attenzione ai cicli di riferimento tra puntatori con conteggio e spezzarli con `Weak` per evitare leak. - Per impostazione predefinita ricorrere al tipo meno potente e lasciare che sia il compilatore a segnalare quando serve maggiore capacità. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/rust/rust-smart-pointers-box-rc-arc-refcell