# Smart pointers do Rust explicados: Box, Rc, Arc e RefCell em 2026 > Os smart pointers do Rust Box, Rc, Arc e RefCell explicados com exemplos compiláveis de 2026, uma tabela de decisão e as perguntas de entrevista mais comuns. - Published: 2026-07-05 - Updated: 2026-07-07 - Author: SharpSkill - Tags: rust, smart-pointers, memory-management, interview, rust-2024-edition - Reading time: 11 min --- Os smart pointers do Rust são as ferramentas que liberam padrões de posse que o borrow checker sozinho não consegue expressar: alocação no heap, posse compartilhada e mutação por trás de uma referência compartilhada. Onde uma referência simples (`&T`) apenas toma emprestado um valor, um smart pointer possui seus dados e adiciona comportamento extra por cima. Este guia detalha os quatro tipos que todo desenvolvedor Rust encontra em produção e em entrevistas: `Box`, `Rc`, `Arc` e `RefCell`, com exemplos compiláveis voltados para a edição Rust 2024. > **O resumo em uma linha** > > Usar `Box` para alocação no heap com um único dono, `Rc` para posse compartilhada em uma única thread, `Arc` para posse compartilhada entre threads, e `RefCell` para mutar um valor através de uma referência compartilhada. Os pares `Rc>` e `Arc>` cobrem o estado mutável compartilhado. ## O que são smart pointers no Rust Um smart pointer é uma struct que se comporta como um ponteiro, mas carrega metadados ou capacidades extras. A maioria implementa a trait `Deref`, de modo que `*pointer` e as chamadas de método funcionam como se o ponteiro fosse uma referência simples, e a trait `Drop`, de modo que a limpeza roda automaticamente quando o valor sai do escopo. A biblioteca padrão fornece os quatro tipos cobertos aqui, e compreendê-los depende de um domínio sólido de [posse e empréstimo](/blog/rust/ownership-borrowing-rust-complete-guide), que decidem quem libera cada alocação e quando. A distinção principal em relação a uma referência é a posse. `&T` nunca possui os dados para os quais aponta, então não pode viver mais que o valor. Um smart pointer possui os dados, controla o tempo de vida deles e os libera de forma determinística. O [capítulo do Rust Book sobre smart pointers](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html) os trata como uma categoria justamente porque compartilham esse formato de posse mais comportamento. ## `Box`: alocação no heap para tipos recursivos e com tamanho `Box` é o smart pointer mais simples. Ele guarda um valor no heap e mantém um ponteiro para ele na pilha, com um único dono e sem sobrecarga em tempo de execução além da própria alocação. Sua tarefa mais comum é dar um tamanho conhecido a tipos recursivos: um tipo que contém a si mesmo diretamente seria infinitamente grande, mas um `Box` é apenas um ponteiro, então seu tamanho é fixo independentemente do que ele aponta. O exemplo abaixo define uma árvore binária. Cada `Node` contém dois filhos, e sem indireção o compilador não consegue calcular o tamanho de `Tree`. Envolver cada filho em um `Box` quebra a recursão no nível do 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` também importa quando mover um valor grande seria custoso, ou quando uma função retorna um objeto de trait como `Box`. Em todos os casos a regra é a mesma: um único dono, liberado automaticamente quando o `Box` é destruído. ## `Rc`: posse compartilhada em código de thread única Às vezes um valor precisa de vários donos, e nenhum deles é claramente o último a usá-lo. `Rc` (contado por referência) resolve isso mantendo uma contagem forte de quantos donos existem. `Rc::clone` incrementa essa contagem sem copiar os dados subjacentes, e cada destruição a decrementa. Quando a contagem chega a zero, o valor é liberado. Considere um objeto de configuração que vários workers leem. Cada worker deve manter a configuração pelo tempo que precisar, e a alocação só deve desaparecer quando o último worker se for. ```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 } ``` Chamar `Rc::clone` explicitamente (em vez de `config.clone()`) é idiomático: sinaliza ao leitor que a operação é barata e só mexe em um contador. A pegadinha é que `Rc` só distribui referências compartilhadas e imutáveis. Ele não pode mutar o valor que compartilha, e não é seguro enviá-lo entre threads. A [documentação de `Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) detalha as duas restrições. ## `RefCell` e a mutabilidade interior no Rust O Rust normalmente impõe que um valor seja compartilhado por muitas referências imutáveis ou mutado por exatamente uma referência mutável, e verifica isso em tempo de compilação. `RefCell` fornece mutabilidade interior: permite que o código mute um valor através de uma referência compartilhada movendo essa mesma verificação para o tempo de execução. `borrow()` retorna um guard de leitura compartilhado; `borrow_mut()` retorna um guard de escrita exclusivo. As regras são idênticas, mas as violações causam um pânico em vez de falhar na compilação. Combinado com `Rc`, isso produz o padrão canônico de mutabilidade compartilhada em thread única, `Rc>`: vários donos que podem todos atualizar o mesmo valor. ```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 move a verificação de empréstimo para o tempo de execução** > > Chamar `borrow_mut()` enquanto outro guard `borrow()` ou `borrow_mut()` ainda está vivo causa um pânico com `already borrowed: BorrowMutError`. As garantias de segurança se mantêm, mas um erro de lógica que o compilador teria detectado vira uma falha em tempo de execução. Convém manter os guards de vida curta e evitar reter um empréstimo através de uma chamada de função que possa reentrar no mesmo `RefCell`. A mutabilidade interior é também onde os ciclos de referência se escondem. Dois valores `Rc>` que apontam um para o outro mantêm a contagem forte um do outro acima de zero para sempre, então nenhum é liberado. A solução é `Weak`, um handle sem posse que não afeta a contagem forte; o passo a passo clássico está em [Learn Rust With Entirely Too Many Linked Lists](https://rust-unofficial.github.io/too-many-lists/). ## `Arc`: contagem de referências thread-safe para concorrência `Arc` (contado por referência de forma atômica) é o irmão multithread de `Rc`. A API pública é quase idêntica, mas o contador usa operações atômicas, de modo que clonar e destruir um `Arc` a partir de várias threads ao mesmo tempo continua correto. Essa atomicidade é o motivo pelo qual `Arc` é escolhido apenas quando o compartilhamento cruza uma fronteira de thread: o incremento atômico é sensivelmente mais lento que o simples que `Rc` usa. Como `Arc` ainda distribui referências imutáveis, mutar entre threads exige uma primitiva de sincronização. `Mutex` é o parceiro habitual, dando o padrão `Arc>` que espelha `Rc>` para o código concorrente. O exemplo dispara quatro threads que incrementam cada uma um total compartilhado mil vezes. ```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 } ``` O compilador impõe a fronteira por você: `Rc` não é `Send`, então tentar mover um para dentro de `thread::spawn` não compila, empurrando na direção de `Arc`. Recorrer a `Arc` dentro de uma única thread apenas paga por atômicos que nunca são usados. A [documentação de `Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) cobre em detalhe as garantias de ordenação de memória, e `Arc>` sustenta boa parte do estado compartilhado no [Rust assíncrono com Tokio](/blog/rust/rust-async-await-tokio-futures-concurrency). ## Box vs Rc vs Arc vs RefCell: quando usar cada um Os quatro tipos se combinam ao longo de dois eixos: quantos donos um valor tem, e se ele pode ser mutado através de um handle compartilhado. A tabela resume os compromissos. | Tipo | Posse | Mutação via handle compartilhado | Thread-safe | Sobrecarga | |------|-----------|----------------------------|-------------|----------| | `Box` | Único | Não | Sim, se `T: Send` | Apenas alocação no heap | | `Rc` | Compartilhada | Não | Não | Contagem não atômica | | `Arc` | Compartilhada | Não | Sim | Contagem atômica | | `RefCell` | Único | Sim (verificada em execução) | Não | Flag de empréstimo em execução | A árvore de decisão prática é curta. Precisa de um único dono no heap: `Box`. Precisa de vários donos em uma única thread: `Rc`. Precisa de vários donos entre threads: `Arc`. Precisa mutar um valor compartilhado: envolver o tipo interno em `RefCell` (thread única) ou `Mutex` (multithread). A orientação do Effective Rust sobre os [tipos de referência e ponteiro](https://www.lurklurk.org/effective-rust/) chega à mesma estratificação. Convém começar pela opção menos poderosa e adicionar capacidade só quando o compilador obrigar. ## Perguntas de entrevista sobre smart pointers do Rust Smart pointers são um tema de entrevista confiável porque testam se um candidato entende posse e não apenas sintaxe. As perguntas abaixo aparecem com frequência, e há mais reunidas no [módulo de entrevista sobre smart pointers do Rust](/technologies/rust/interview-questions/smart-pointers). **Qual é a diferença entre `Rc` e `Arc`?** Ambos fornecem posse compartilhada por contagem de referências. `Rc` usa um contador não atômico e fica confinado a uma única thread; `Arc` usa operações atômicas e pode ser compartilhado entre threads a um pequeno custo em execução. O compilador impõe a divisão: `Rc` não é nem `Send` nem `Sync`, então não pode cruzar uma fronteira de thread. **Por que combinar `Rc` com `RefCell`?** `Rc` concede posse compartilhada, mas apenas acesso imutável. `RefCell` adiciona mutabilidade interior, permitindo que os donos mutem o valor através de uma referência compartilhada com verificações de empréstimo em execução. Juntos, `Rc>` é o bloco idiomático de mutabilidade compartilhada em thread única. **Como a contagem de referências pode vazar memória no Rust?** Dois valores `Rc` (ou `Arc`) que se referenciam mutuamente formam um ciclo cujas contagens fortes nunca chegam a zero, então a alocação nunca é liberada. Quebrar o ciclo com `Weak`, que mantém uma referência sem posse, restaura a limpeza correta. **Quando `Box` é preferível a `Rc`?** Sempre que um único dono for suficiente. `Box` não tem sobrecarga de contagem de referências, então é a escolha padrão para alocação no heap, tipos recursivos e objetos de trait. Só convém recorrer a `Rc` quando posse compartilhada real for necessária. ## Conclusão Os smart pointers do Rust transformam a posse, de restrição em um conjunto de blocos combináveis. A escolha entre eles decorre diretamente do formato do problema: - Recorrer a `Box` quando um valor precisa de um único dono no heap, quando um tipo recursivo precisa de tamanho fixo, ou quando uma função retorna um objeto de trait. - Usar `Rc` para vários donos em uma única thread, e mudar para `Arc` no momento em que a posse cruza uma fronteira de thread. - Adicionar `RefCell` para mutabilidade interior em código de thread única, e `Mutex` para o equivalente concorrente, mantendo curtos os guards de empréstimo e de bloqueio. - Combiná-los deliberadamente: `Rc>` para estado mutável compartilhado em uma única thread, `Arc>` entre threads. - Ficar atento a ciclos de referência entre ponteiros contados e quebrá-los com `Weak` para evitar vazamentos. - Optar por padrão pelo tipo menos poderoso e deixar o compilador indicar quando mais capacidade é necessária. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pt/blog/rust/rust-smart-pointers-box-rc-arc-refcell