<h2>Rc<T>: Contagem de Referências em Single-Thread</h2>
<p><code>Rc<T></code> (Reference Counting) é um tipo de dado que permite múltiplos proprietários para um mesmo valor em um programa single-thread. Diferente do sistema de ownership padrão do Rust, onde apenas um dono é permitido, <code>Rc<T></code> mantém um contador interno que rastreia quantas referências imutáveis apontam para os dados. Quando o contador chega a zero, a memória é automaticamente liberada.</p>
<p>O caso de uso clássico é quando você precisa compartilhar dados entre múltiplas partes do código sem transferir ownership. Considere uma estrutura de árvore onde múltiplos nós precisam apontar para o mesmo nó pai: é exatamente o cenário onde <code>Rc<T></code> brilha. A desvantagem é que você só consegue referências imutáveis; se precisar modificar dados, deve combinar com <code>RefCell<T></code>.</p>
<pre><code class="language-rust">use std::rc::Rc;
struct Node {
value: i32,
children: Vec<Rc<Node>>,
}
impl Node {
fn new(value: i32) -> Self {
Node {
value,
children: Vec::new(),
}
}
}
fn main() {
let shared_node = Rc::new(Node::new(42));
// Clone cria uma nova referência, não copia os dados
let ref1 = Rc::clone(&shared_node);
let ref2 = Rc::clone(&shared_node);
println!("Contador de referências: {}", Rc::strong_count(&shared_node));
// Saída: 3 (shared_node + ref1 + ref2)
drop(ref1);
println!("Após drop: {}", Rc::strong_count(&shared_node));
// Saída: 2
}</code></pre>
<h3>Por que não usar <code>Rc<T></code> em Multi-thread?</h3>
<p><code>Rc<T></code> usa operações não-atômicas para incrementar/decrementar o contador. Em um ambiente multi-thread, múltiplas threads poderiam modificar o contador simultaneamente, causando race conditions. O compilador Rust impede isso: <code>Rc<T></code> não implementa <code>Send</code> nem <code>Sync</code>, então você simplesmente não consegue compartilhá-lo entre threads.</p>
<h2>Arc<T>: Atomic Reference Counting para Multi-thread</h2>
<p><code>Arc<T></code> (Atomic Reference Counting) é a versão thread-safe de <code>Rc<T></code>. Usa operações atômicas para gerenciar o contador, garantindo que múltiplas threads possam acessar o mesmo dado simultaneamente sem race conditions. A troca é um pequeno overhead de performance, compensado pela segurança.</p>
<p><code>Arc<T></code> é essencial quando você precisa compartilhar dados entre threads de forma segura. Um padrão comum é usá-lo com <code>Mutex<T></code> para permitir modificações thread-safe. Sozinho, <code>Arc<T></code> oferece apenas referências imutáveis, mas a combinação <code>Arc<Mutex<T>></code> é poderosa: você consegue propriedade compartilhada com acesso exclusivo para modificação.</p>
<pre><code class="language-rust">use std::sync::Arc;
use std::thread;
fn main() {
let counter = Arc::new(0);
let mut handles = vec![];
for _ in 0..3 {
let counter_clone = Arc::clone(&counter);
let handle = thread::spawn(move || {
println!("Thread com contador: {:?}", counter_clone);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}</code></pre>
<h3>Arc<T> com Mutex<T></h3>
<p>Para modificar dados compartilhados entre threads, combine <code>Arc<Mutex<T>></code>. O <code>Mutex</code> garante que apenas uma thread acessa os dados por vez, enquanto <code>Arc</code> permite que múltiplas threads possuam a referência.</p>
<pre><code class="language-rust">use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..5 {
let counter_clone = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter_clone.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Resultado: {}", *counter.lock().unwrap());
// Saída: 5
}</code></pre>
<h2>Comparação Prática: Quando Usar Cada Um</h2>
<p><code>Rc<T></code> é a escolha para programas single-thread onde você precisa de múltiplos donos. Sua implementação é mais eficiente porque não requer atomicidade. Use quando: estruturas de dados complexas (grafos, árvores), caching compartilhado ou componentes que precisam acessar um recurso comum sem transferência de ownership.</p>
<p><code>Arc<T></code> é obrigatório em multi-threading. O overhead de operações atômicas é negligenciável comparado aos benefícios de segurança. Use quando: compartilhar estado entre threads, implementar workers em thread pools ou quando dados precisam ser acessíveis de múltiplos lugares simultaneamente em execução paralela.</p>
<div class="table-wrap"><table><thead><tr><th>Aspecto</th><th>Rc<T></th><th>Arc<T></th></tr></thead><tbody><tr><td>Single-thread</td><td>✓</td><td>✓</td></tr><tr><td>Multi-thread</td><td>✗</td><td>✓</td></tr><tr><td>Overhead</td><td>Mínimo</td><td>Atomicidade</td></tr><tr><td>Segurança em paralelo</td><td>Nenhuma</td><td>Total</td></tr><tr><td>Caso de uso</td><td>Compartilhamento local</td><td>Compartilhamento global</td></tr></tbody></table></div>
<pre><code class="language-rust">// Rc para single-thread: grafo de dependências
use std::rc::Rc;
struct Package {
name: String,
depends_on: Vec<Rc<Package>>,
}
// Arc para multi-thread: processamento distribuído
use std::sync::Arc;
use std::thread;
let data = Arc::new(vec![1, 2, 3, 4, 5]);
let handles: Vec<_> = (0..4).map(|i| {
let d = Arc::clone(&data);
thread::spawn(move || {
println!("Thread {} vê: {:?}", i, d);
})
}).collect();</code></pre>
<h2>Conclusão</h2>
<p><code>Rc<T></code> e <code>Arc<T></code> resolvem um dos maiores desafios em linguagens de baixo nível: compartilhar dados mantendo segurança de memória. <strong>Primeiro ponto</strong>: <code>Rc<T></code> é para single-thread e usa contagem não-atômica; <code>Arc<T></code> é para multi-thread com contagem atômica. <strong>Segundo ponto</strong>: ambos oferecem apenas referências imutáveis por padrão—combine com <code>RefCell<T></code> ou <code>Mutex<T></code> para mutabilidade. <strong>Terceiro ponto</strong>: escolha baseado em seu contexto (threads ou não) e o compilador Rust o guiará; ele impede misturar tipos inadequados em tempo de compilação.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://doc.rust-lang.org/book/ch15-00-smart-pointers.html" target="_blank" rel="noopener noreferrer">The Rust Programming Language - Smart Pointers</a></li>
<li><a href="https://doc.rust-lang.org/std/rc/struct.Rc.html" target="_blank" rel="noopener noreferrer">Rust std::rc::Rc Documentation</a></li>
<li><a href="https://doc.rust-lang.org/std/sync/struct.Arc.html" target="_blank" rel="noopener noreferrer">Rust std::sync::Arc Documentation</a></li>
<li><a href="https://doc.rust-lang.org/book/ch16-00-concurrency.html" target="_blank" rel="noopener noreferrer">Fearless Concurrency - The Rust Book</a></li>
<li><a href="https://www.oreilly.com/library/view/programming-rust-2nd/9781492052586/" target="_blank" rel="noopener noreferrer">Programming Rust: Fast, Safe Systems Development - Jim Blandy & Jason Orendorff</a></li>
</ul>