---
title: "Rc\u003cT\u003e e Arc\u003cT\u003e: Contagem de Referências"
url: "https://rustlang.com.br/stdlib/rc-arc/"
markdown_url: "https://rustlang.com.br/stdlib/rc-arc.MD"
description: "Guia completo de Rc\u003cT\u003e e Arc\u003cT\u003e em Rust: ownership compartilhado, referências fracas, Arc+Mutex para threads e padrões práticos."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Rc<T> e Arc<T>: Contagem de Referências

Guia completo de Rc<T> e Arc<T> em Rust: ownership compartilhado, referências fracas, Arc+Mutex para threads e padrões práticos.


## O que são Rc\<T\> e Arc\<T\>

`Rc<T>` (Reference Counted) e `Arc<T>` (Atomically Reference Counted) são smart pointers que permitem **múltiplos donos** para o mesmo valor. Cada vez que você clona um `Rc` ou `Arc`, apenas o contador de referências é incrementado — os dados não são copiados. Quando o último dono é liberado (o contador chega a zero), a memória é desalocada automaticamente.

A diferença crucial entre eles é: `Rc<T>` funciona **apenas em single-thread** (não implementa `Send` nem `Sync`), enquanto `Arc<T>` usa operações atômicas no contador, tornando-o seguro para uso **entre threads**. O `Arc` tem um pequeno custo extra de performance por causa da sincronização atômica. Use `Rc` quando possível e `Arc` apenas quando precisar compartilhar dados entre threads.

---

## Criando Rc e Arc

```rust
use std::rc::Rc;
use std::sync::Arc;

fn main() {
    // Rc<T> — single thread
    let rc1 = Rc::new(String::from("compartilhado"));
    let rc2 = Rc::clone(&rc1); // Incrementa contador, NÃO clona a String
    let rc3 = rc1.clone();     // Equivalente a Rc::clone

    println!("Valor: {rc1}");
    println!("Referências: {}", Rc::strong_count(&rc1)); // 3

    drop(rc3);
    println!("Após drop: {}", Rc::strong_count(&rc1)); // 2

    // Arc<T> — thread-safe
    let arc1 = Arc::new(vec![1, 2, 3]);
    let arc2 = Arc::clone(&arc1);

    println!("Arc refs: {}", Arc::strong_count(&arc1)); // 2
    println!("Dados: {arc2:?}");
}
```

---

## Tabela de Métodos Principais

### Rc\<T\>

| Método | Descrição | Exemplo |
|---|---|---|
| `Rc::new(val)` | Cria um novo Rc | `Rc::new(42)` |
| `Rc::clone(&rc)` | Incrementa a contagem (sem cópia de dados) | `Rc::clone(&meu_rc)` |
| `Rc::strong_count(&rc)` | Número de referências fortes | `Rc::strong_count(&rc)` → `3` |
| `Rc::weak_count(&rc)` | Número de referências fracas | `Rc::weak_count(&rc)` → `1` |
| `Rc::downgrade(&rc)` | Cria uma referência fraca (`Weak<T>`) | `Rc::downgrade(&rc)` |
| `Rc::try_unwrap(rc)` | Extrai o valor se há exatamente 1 ref | `Rc::try_unwrap(rc).ok()` |
| `Rc::into_inner(rc)` | Extrai se é o último dono (Rust 1.70+) | `Rc::into_inner(rc)` |
| `Rc::ptr_eq(&a, &b)` | Compara ponteiros (mesma alocação?) | `Rc::ptr_eq(&rc1, &rc2)` |

### Arc\<T\>

| Método | Descrição | Exemplo |
|---|---|---|
| `Arc::new(val)` | Cria um novo Arc | `Arc::new(42)` |
| `Arc::clone(&arc)` | Incrementa a contagem atomicamente | `Arc::clone(&meu_arc)` |
| `Arc::strong_count(&arc)` | Número de referências fortes | `Arc::strong_count(&arc)` |
| `Arc::weak_count(&arc)` | Número de referências fracas | `Arc::weak_count(&arc)` |
| `Arc::downgrade(&arc)` | Cria `Weak<T>` | `Arc::downgrade(&arc)` |
| `Arc::try_unwrap(arc)` | Extrai se há exatamente 1 ref | `Arc::try_unwrap(arc).ok()` |
| `Arc::ptr_eq(&a, &b)` | Compara ponteiros | `Arc::ptr_eq(&a1, &a2)` |

### Weak\<T\>

| Método | Descrição | Exemplo |
|---|---|---|
| `weak.upgrade()` | Tenta obter `Option<Rc<T>>` ou `Option<Arc<T>>` | `weak.upgrade()` |
| `weak.strong_count()` | Contagem de refs fortes | `weak.strong_count()` |

---

## Exemplos Práticos

### 1. Compartilhando Dados entre Estruturas

```rust
use std::rc::Rc;

#[derive(Debug)]
struct Cidade {
    nome: String,
    populacao: u64,
}

#[derive(Debug)]
struct Empresa {
    nome: String,
    sede: Rc<Cidade>,
}

#[derive(Debug)]
struct Universidade {
    nome: String,
    cidade: Rc<Cidade>,
}

fn main() {
    let sp = Rc::new(Cidade {
        nome: "São Paulo".into(),
        populacao: 12_300_000,
    });

    let empresa = Empresa {
        nome: "TechBR".into(),
        sede: Rc::clone(&sp),
    };

    let uni = Universidade {
        nome: "USP".into(),
        cidade: Rc::clone(&sp),
    };

    println!("{} tem sede em {}", empresa.nome, empresa.sede.nome);
    println!("{} fica em {}", uni.nome, uni.cidade.nome);
    println!("Referências a SP: {}", Rc::strong_count(&sp)); // 3

    // Comparação de ponteiros — são o MESMO objeto
    assert!(Rc::ptr_eq(&empresa.sede, &uni.cidade));
}
```

### 2. Referências Fracas para Evitar Ciclos

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

#[derive(Debug)]
struct No {
    valor: i32,
    filhos: RefCell<Vec<Rc<No>>>,
    pai: RefCell<Weak<No>>,
}

impl No {
    fn novo(valor: i32) -> Rc<Self> {
        Rc::new(No {
            valor,
            filhos: RefCell::new(Vec::new()),
            pai: RefCell::new(Weak::new()),
        })
    }

    fn adicionar_filho(pai: &Rc<No>, filho: &Rc<No>) {
        pai.filhos.borrow_mut().push(Rc::clone(filho));
        *filho.pai.borrow_mut() = Rc::downgrade(pai);
    }
}

fn main() {
    let raiz = No::novo(1);
    let filho_a = No::novo(2);
    let filho_b = No::novo(3);

    No::adicionar_filho(&raiz, &filho_a);
    No::adicionar_filho(&raiz, &filho_b);

    // Navegar de filho para pai
    if let Some(pai) = filho_a.pai.borrow().upgrade() {
        println!("Pai do nó {}: {}", filho_a.valor, pai.valor); // Pai do nó 2: 1
    }

    println!("Raiz refs fortes: {}", Rc::strong_count(&raiz)); // 1
    println!("Raiz refs fracas: {}", Rc::weak_count(&raiz));   // 2

    // Sem Weak, haveria um ciclo pai→filho→pai que impediria a desalocação
}
```

### 3. Arc + Mutex para Dados Compartilhados entre Threads

```rust
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let contador = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for i in 0..10 {
        let contador = Arc::clone(&contador);
        let handle = thread::spawn(move || {
            let mut num = contador.lock().unwrap();
            *num += 1;
            println!("Thread {i}: contador = {num}");
        });
        handles.push(handle);
    }

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

    println!("Resultado final: {}", *contador.lock().unwrap()); // 10
}
```

### 4. Cache Compartilhado com Arc + RwLock

```rust
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::thread;

type Cache = Arc<RwLock<HashMap<String, String>>>;

fn buscar_com_cache(cache: &Cache, chave: &str) -> String {
    // Tenta ler primeiro (múltiplas threads podem ler simultaneamente)
    {
        let leitura = cache.read().unwrap();
        if let Some(valor) = leitura.get(chave) {
            return valor.clone();
        }
    } // Lock de leitura liberado aqui

    // Se não encontrou, simula busca e salva no cache
    let valor = format!("resultado_para_{chave}");
    {
        let mut escrita = cache.write().unwrap();
        escrita.insert(chave.to_string(), valor.clone());
    }
    valor
}

fn main() {
    let cache: Cache = Arc::new(RwLock::new(HashMap::new()));
    let mut handles = vec![];

    let chaves = vec!["user:1", "user:2", "user:1", "user:3", "user:2"];

    for chave in chaves {
        let cache = Arc::clone(&cache);
        let chave = chave.to_string();
        let handle = thread::spawn(move || {
            let resultado = buscar_com_cache(&cache, &chave);
            println!("[{chave}] → {resultado}");
        });
        handles.push(handle);
    }

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

    let leitura = cache.read().unwrap();
    println!("\nCache final: {} entradas", leitura.len());
    for (k, v) in leitura.iter() {
        println!("  {k}: {v}");
    }
}
```

### 5. Padrão Observer com Rc e Weak

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

trait Observador {
    fn notificar(&self, mensagem: &str);
}

struct EventBus {
    ouvintes: RefCell<Vec<Weak<dyn Observador>>>,
}

impl EventBus {
    fn novo() -> Self {
        EventBus {
            ouvintes: RefCell::new(Vec::new()),
        }
    }

    fn registrar(&self, ouvinte: &Rc<dyn Observador>) {
        self.ouvintes.borrow_mut().push(Rc::downgrade(ouvinte));
    }

    fn emitir(&self, mensagem: &str) {
        let mut ouvintes = self.ouvintes.borrow_mut();
        // Remove ouvintes que já foram desalocados e notifica os ativos
        ouvintes.retain(|weak| {
            if let Some(ouvinte) = weak.upgrade() {
                ouvinte.notificar(mensagem);
                true
            } else {
                false // Ouvinte foi liberado, remover da lista
            }
        });
    }
}

struct Logger {
    prefixo: String,
}

impl Observador for Logger {
    fn notificar(&self, mensagem: &str) {
        println!("[{}] {mensagem}", self.prefixo);
    }
}

fn main() {
    let bus = EventBus::novo();

    let logger1: Rc<dyn Observador> = Rc::new(Logger {
        prefixo: "INFO".into(),
    });
    let logger2: Rc<dyn Observador> = Rc::new(Logger {
        prefixo: "DEBUG".into(),
    });

    bus.registrar(&logger1);
    bus.registrar(&logger2);

    bus.emitir("Sistema iniciado");
    // [INFO] Sistema iniciado
    // [DEBUG] Sistema iniciado

    drop(logger2); // Remove o logger DEBUG

    bus.emitir("Processando requisição");
    // [INFO] Processando requisição
    // (logger2 foi automaticamente removido)
}
```

---

## Dicas de Performance e Armadilhas

1. **Rc não é thread-safe**: Tentar enviar um `Rc` entre threads gera erro de compilação. Use `Arc` para cenários multi-thread.

2. **Rc/Arc são imutáveis por padrão**: Para mutabilidade interna, combine com `Cell`, `RefCell` (single-thread) ou `Mutex`, `RwLock` (multi-thread).

3. **Ciclos de referência**: `Rc` e `Arc` **não detectam ciclos**. Se A aponta para B e B aponta para A, a memória nunca é liberada. Use `Weak` para quebrar ciclos.

4. **Clone é barato, mas não gratuito**: `Rc::clone` e `Arc::clone` incrementam um contador. Para `Arc`, é uma operação atômica — mais cara que `Rc` mas ainda muito rápida.

5. **Prefira `Rc::clone(&rc)` a `rc.clone()`**: A forma `Rc::clone(&rc)` deixa claro que estamos incrementando a contagem, não clonando os dados. É uma convenção importante em Rust.

6. **`Arc<Mutex<T>>` é o padrão**: Para dados compartilhados e mutáveis entre threads, `Arc<Mutex<T>>` é o padrão mais comum. Para leituras frequentes, considere `Arc<RwLock<T>>`.

---

## Veja Também

- [Smart Pointers em Rust](/artigos/smart-pointers/) — visão geral de todos os smart pointers
- [Concorrência em Rust](/tutoriais/concorrencia/) — tutorial de programação concorrente
- [Box\<T\>: Alocação no Heap](/stdlib/box-t/) — smart pointer com dono único
- [Cow\<T\>: Clone on Write](/stdlib/cow/) — alternativa para evitar clonagens
- [Iterator Trait](/stdlib/iterator/) — para processar dados compartilhados
- [HashMap\<K,V\>](/stdlib/hashmap/) — frequentemente usado com Arc+Mutex
- [Cheatsheet Rust](/cheatsheet/) — referência rápida de sintaxe
- [Documentação oficial — std::rc::Rc](https://doc.rust-lang.org/std/rc/struct.Rc.html)
- [Documentação oficial — std::sync::Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html)
