Rust: Ponteiros inteligentes em Rust

Última atualização: 2026-08-26

Um ponteiro inteligente é uma “estrutura de dados com comportamento adicional” — ele faz referência à memória da mesma forma que um ponteiro, mas oferece recursos que vão além dos de um ponteiro comum, como gerenciamento automático de memória, contagem de referências e verificação de empréstimo.

Se um ponteiro comum é como “uma folha de papel com um endereço escrito nela”, então um ponteiro inteligente é como “um sistema de gestão imobiliária que conta com seus próprios seguranças, zeladores e livro-razão”. Os ponteiros inteligentes do Rust garantem segurança em tempo de compilação e não apresentam sobrecarga em tempo de execução (além da sobrecarga mínima da contagem de referências).


1. O que você vai aprender


2. A história das mesas compartilhadas

(1) Problema: Vários usuários desejam visualizar e editar o mesmo documento ao mesmo tempo

Na manhã de segunda-feira, a empresa da Luna adotou uma política de “mesas compartilhadas”. Havia apenas três mesas para uma equipe de cinco pessoas.

“Se ao menos soubéssemos quantas pessoas ainda estão acessando este documento, poderíamos simplesmente destruí-lo assim que ninguém mais o estivesse acessando...” “Se ao menos pudéssemos garantir que, quando uma pessoa estiver editando-o, ninguém mais possa fazer alterações...”

(2) Soluções com ponteiros inteligentes em Rust

RUST
use std::rc::Rc;
use std::cell::RefCell;

fn main() {
    // Box: One person has exclusive access to a document,Destroy after reading
    let doc_box = Box::new(String::from("Project Proposal v1"));
    println!("Box Hold: {}", doc_box);
    // doc_box Automatically destroyed upon leaving the scope——No manual intervention required free

    // Rc: Shared Read-Only Access for Multiple Users
    let doc_rc = Rc::new(String::from("Shared Project Proposal"));
    let alice = Rc::clone(&doc_rc);
    let bob = Rc::clone(&doc_rc);
    println!("Reference Count: {}", Rc::strong_count(&doc_rc)); // 3

    // Rc<RefCell<T>>: Share + Variable
    let doc_shared = Rc::new(RefCell::new(String::from("Collaborative Documents")));
    let alice_view = Rc::clone(&doc_shared);
    let bob_view = Rc::clone(&doc_shared);

    // Bob Add annotations to a document(Needs to be revised)
    bob_view.borrow_mut().push_str("\nBob Comments on:Part 2 needs to be completed");
    // Alice View the documentation(Read-only)
    println!("Alice See: {}", alice_view.borrow());
    // Carol See the documentation as well(Read-only)
    println!("Carol See: {}", doc_shared.borrow());
}

O Rust utiliza três tipos de ponteiros inteligentes para lidar com questões de segurança entre threads relacionadas ao “compartilhamento” e à “modificação”: Box propriedade exclusiva e alocação na pilha, Rc propriedade compartilhada somente para leitura e RefCell mutabilidade interna obtida por meio de verificações em tempo de execução das regras de empréstimo.


3. Conceitos fundamentais

(1) Sistema de Ponteiros Inteligentes

100%
graph TB
    A[Rust Smart Pointers] --> B[Box&lt;T&gt; Heap Allocation]
    A --> C[Rc&lt;T&gt; Reference Count]
    A --> D[RefCell&lt;T&gt; Internal Variability]

    B --> B1["let b = Box::new(42)"]
    B --> B2["Automatically leaves scope drop"]

    C --> C1["Rc::clone Increase the reference count"]
    C --> C2["strong_count == 0 Destroy on time"]
    C --> C3["Use in a single-threaded environment"]

    D --> D1["borrow() → Immutable References"]
    D --> D2["borrow_mut() → Variable References"]
    D --> D3["Runtime Check of Borrowing Rules"]

    A --> E[Combination Mode]
    E --> E1["Rc&lt;RefCell&lt;T&gt;&gt;"]
    E --> E2["Shared Ownership + Internal Variability"]

    A --> F[Core Trait]
    F --> F1["Deref: Dereference Operator *"]
    F --> F2["Drop: Destructor"]

(2) Comparação entre três tipos de agulhas inteligentes

Propriedade Box<T> Rc<T> RefCell<T>
Propriedade Propriedade única Propriedade compartilhada (contagem de referências) Propriedade única
Mutabilidade Mutável (Box::new seguido por *b = val) Imutável (compartilhado somente para leitura) Mutável internamente (verificado em tempo de execução)
Verificação de tempo Tempo de compilação Tempo de compilação Tempo de execução
Sobrecarga de desempenho Sem sobrecarga adicional Alterações na contagem de referências (mínimas) Verificações de empréstimo em tempo de execução (mínimas)
Seguro para múltiplas threads Sim Não (mono-thread) Não (mono-thread)
Casos de uso Conjuntos de dados grandes alocados na pilha, tipos recursivos Dados compartilhados de leitura exclusiva com múltiplos caminhos Necessidade de modificar dados sob referências imutáveis

(3) Traços “Deref” e “Drop”

Característica Método Função
Deref fn deref(&self) -> &T Permitir que as operações de desreferenciamento *x sejam aplicadas a tipos personalizados
Drop fn drop(&mut self) Lógica de limpeza chamada automaticamente quando um valor sai de seu escopo

(4) Comparação entre os modelos Box, Rc e Arc

Características Box<T> Rc<T> Arc<T>
Propriedade Proprietário único Vários proprietários (contagem de referências) Vários proprietários (contagem atômica de referências)
Seguro para threads Não (apenas para uma única thread) Não (apenas para uma única thread) Sim (pode ser compartilhado entre threads)
Contagem de referências Nenhuma Não atômica, baixa sobrecarga Operação atômica, sobrecarga ligeiramente maior
Acesso à variável &mut Variável diretamente Requer RefCell Requer Mutex / RwLock
Cenários típicos Tipos recursivos / Alocação em heap para Big Data DAG / Dados compartilhados somente para leitura Compartilhamento de dados em multithread
Desempenho Mais rápido Média Um pouco mais lento (operações atômicas)

Deref Faz com que os ponteiros inteligentes funcionem exatamente como referências comuns — Box<T> pode ser usado da mesma forma que &T. Drop garante que os recursos sejam liberados automaticamente — não há necessidade de free ou delete manualmente.


4. Exemplos de ponteiros inteligentes

▶ Exemplo 1: Box — Alocação em heap e tipos recursivos (Dificuldade ⭐)

RUST
// ============================================
// Box<T> Three Typical Uses of:Heap Allocation、Recursive Types、Deref
// ============================================

// Recursive Types:Cons list(Must use Box,Because the size is unknown at compile time)
#[derive(Debug)]
enum List {
    Cons(i32, Box<List>),
    Nil,
}

use List::{Cons, Nil};

// Custom Smart Pointers (Demo Deref and Drop)
use std::ops::Deref;

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0  // A reference to the internal value
    }
}

impl<T> Drop for MyBox<T> {
    fn drop(&mut self) {
        // In practical applications, resources are released here(For example, closing a file、Release the network connection)
        // println!("MyBox It was destroyed.~");
    }
}

fn main() {
    // --- 1. Box Basic Heap Allocation ---
    let b = Box::new(42);
    println!("Box the value in: {}", b);  // Automatic Dereferencing

    // --- 2. Recursive Types:Cons List ---
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("Recursive List: {:?}", list);

    // --- 3. Custom MyBox + Deref ---
    let my_box = MyBox::new(String::from("Hello"));
    // Deref lets &MyBox<String> automatically convert to &String, then to &str
    greet(&my_box);

    // --- 4. Dereference Operator ---
    let x = 10;
    let y = MyBox::new(x);
    assert_eq!(10, *y);  // *y Equivalent to *(y.deref())
    println!("*y == {}", *y);
}

fn greet(name: &str) {
    println!("Greeting: {}", name);
}

Resultado:

TEXT 📖 Somente leitura
Box the value in: 42
Recursive List: Cons(1, Cons(2, Cons(3, Nil)))
Greeting: Hello
*y == 10

O valor central do Box<T> é duplo: primeiro, ele aloca os dados na pilha (retendo apenas ponteiros na pilha); segundo, permite que tipos cujo tamanho é desconhecido em tempo de compilação (como tipos recursivos) sejam usados normalmente. A característica Deref permite que Box<T> seja desreferenciado exatamente como &T — é daí que vem o termo “inteligente” em “ponteiro inteligente”.


▶ Exemplo 2: Rc — Contagem de referências com propriedade compartilhada (Dificuldade ⭐⭐)

RUST
// ============================================
// Rc<T> Reference Count:Multipath Sharing of Read-Only Data
// ============================================

use std::rc::Rc;

#[derive(Debug)]
enum BookList {
    Cons(String, Rc<BookList>),
    Nil,
}

use BookList::{Cons, Nil};

fn main() {
    // --- Scene:Three books are shared by two readers ---

    // Create a Basic Reading List
    let book_c = Rc::new(Cons("Rust Programming".to_string(),
        Rc::new(Cons("Introduction to Algorithms".to_string(),
            Rc::new(Cons("Design Patterns".to_string(),
                Rc::new(Nil))))));

    println!("Initial reference count: {}", Rc::strong_count(&book_c));

    // Alice Borrowed the reading list(Rc::clone Increase the reference count only,Do not deep-copy data)
    let alice = Rc::clone(&book_c);
    println!("Alice Reference Count After Borrowing: {}", Rc::strong_count(&book_c));

    {
        // Bob I also borrowed the reading list(In the inner scope)
        let bob = Rc::clone(&book_c);
        println!("Bob Reference Count After Borrowing: {}", Rc::strong_count(&book_c));

        // Both of them can read it.
        println!("Alice Book List: {:?}", *alice);
        println!("Bob Book List: {:?}", *bob);
    }  // Bob Out of scope,Decrease in reference count

    println!("Bob Citation Count After Returning the Book: {}", Rc::strong_count(&book_c));

    // Alice Still reading
    println!("Alice Still reading: {:?}", *alice);

    // All Rc After they have all gone out of scope,Only then is the data truly destroyed
    // Rc::strong_count == 0 When triggered drop
}

Resultado:

TEXT 📖 Somente leitura
Initial reference count: 1
Alice Reference Count After Borrowing: 2
Bob Reference Count After Borrowing: 3
Alice Book List: Cons("Rust Programming", Cons("Introduction to Algorithms", Cons("Design Patterns", Nil)))
Bob Book List: Cons("Rust Programming", Cons("Introduction to Algorithms", Cons("Design Patterns", Nil)))
Bob Citation Count After Returning the Book: 2
Alice Still reading: Cons("Rust Programming", Cons("Introduction to Algorithms", Cons("Design Patterns", Nil)))

Rc<T> significa “Contagem de Referências”. Rc::clone(&x) não realiza uma cópia profunda dos dados; simplesmente incrementa a contagem de referências em 1. Os dados só são destruídos quando todos os identificadores Rc saem do escopo (e a contagem de referências chega a zero). Rc só pode ser usado em um ambiente de thread único — para multithreading, use Arc<T>.


▶ Exemplo 3: RefCell — Variabilidade interna (Dificuldade ⭐⭐)

RUST
// ============================================
// RefCell<T>:Runtime Borrowing Checks + Internal Variability
// ============================================

use std::cell::RefCell;

// Simulate a "Log Entry" Messenger
// External code can only obtain immutable references, but the log needs to be modified internally.
trait Messenger {
    fn send(&self, msg: &str);
}

// Logger:For internal use only RefCell Store Messages
struct Logger {
    // Even if Logger Inherently immutable,messages It can still be edited
    messages: RefCell<Vec<String>>,
}

impl Logger {
    fn new() -> Logger {
        Logger {
            messages: RefCell::new(Vec::new()),
        }
    }
}

impl Messenger for Logger {
    fn send(&self, msg: &str) {
        // borrow_mut() gets a mutable reference — even if self is &self
        self.messages.borrow_mut().push(msg.to_string());
    }
}

fn main() {
    let logger = Logger::new();

    // Calling via an immutable reference send——But it has actually been modified internally.
    logger.send("User Login");
    logger.send("Click the button");
    logger.send("Data submitted successfully");

    // borrow() Get an immutable reference
    let msgs = logger.messages.borrow();
    for (i, msg) in msgs.iter().enumerate() {
        println!("Log #{}: {}", i + 1, msg);
    }

    // --- Examples of Runtime Borrowing Violations(Cancel the Commentary Session panic)---
    // let mut_ref = logger.messages.borrow_mut();
    // let ref1 = logger.messages.borrow();    // panic! Both mutable and immutable references
    // println!("{}", ref1[0]);

    println!("RefCell The presentation is over.");
}

Resultado:

TEXT 📖 Somente leitura
Log #1: User Login
Log #2: Click the button
Log #3: Data submitted successfully
RefCell The presentation is over.

O princípio central do RefCell<T> é a “mutabilidade interna”: mesmo que o próprio Logger seja uma referência imutável ao &self, o RefCell ainda permite que você modifique os dados internos. A diferença é que as regras de empréstimo não são verificadas em tempo de compilação, mas sim em tempo de execução — se uma regra for violada, o programa entrará em pânico em vez de gerar um erro em tempo de compilação.


▶ Exemplo 4: Rc<RefCell<T>> — Compartilhado + Mutável (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Rc<RefCell<T>> Combination Mode:Shared Ownership + Internal Variability
// ============================================

use std::rc::Rc;
use std::cell::RefCell;

// Simulate a"Shared Whiteboard"——Team members can write and read
#[derive(Debug)]
struct Whiteboard {
    content: RefCell<String>,
}

impl Whiteboard {
    fn new() -> Whiteboard {
        Whiteboard {
            content: RefCell::new(String::new()),
        }
    }

    fn write(&self, text: &str) {
        let mut content = self.content.borrow_mut();
        content.push_str(text);
        content.push('\n');
    }

    fn read(&self) -> String {
        self.content.borrow().clone()
    }
}

fn main() {
    // Create a shared whiteboard, wrapped with Rc so it can be held by multiple people
    let board = Rc::new(Whiteboard::new());

    // Alice and Bob both hold references to the whiteboard
    let alice_board = Rc::clone(&board);
    let bob_board = Rc::clone(&board);
    let carol_board = Rc::clone(&board);

    println!("Current reference count: {}", Rc::strong_count(&board));  // 4

    // Alice Write on the whiteboard
    alice_board.write("Alice: Today's Discussion Rust Smart Pointers");
    // Bob Additional Information
    bob_board.write("Bob: I'll explain the difference between Box and Rc");
    // Carol Write as well
    carol_board.write("Carol: RefCell Runtime borrowing checks are important");

    // Everyone can view the full content(Because they share the same RefCell)
    println!("=== Whiteboard Content ===");
    println!("{}", board.read());

    // Verify that all references point to the same data
    println!("Alice Length of content viewed: {}", alice_board.read().len());
    println!("Bob Length of content viewed: {}", bob_board.read().len());
    println!("Carol Length of content viewed: {}", carol_board.read().len());

    // board When leaving the scope,Reference Count Reset to Zero,Whiteboard Automatic Destruction
}

Resultado:

TEXT 📖 Somente leitura
Current reference count: 4
=== Whiteboard Content ===
Alice: Today's Discussion Rust Smart Pointers
Bob: I'll explain the difference between Box and Rc
Carol: RefCell Runtime borrowing checks are important

Alice Length of content viewed: 73
Bob Length of content viewed: 73
Carol Length of content viewed: 73

Rc<RefCell<T>> é um dos padrões composíveis mais poderosos na programação em Rust com um único thread: Rc resolve o problema de “várias pessoas quererem manter” algo, enquanto RefCell resolve o problema de “pessoas que mantêm referências imutáveis também quererem modificá-lo”. Se compararmos Rc a um “cartão de biblioteca compartilhado”, então RefCell é uma “cópia especial que permite anotações”.


▶ Exemplo 5: Exercício abrangente — Estruturas de dados em grafos (Dificuldade ⭐⭐⭐)

RUST
// ============================================
// Comprehensive Example:Rc<RefCell<T>> Graph Node Implementation
// ============================================

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

struct Node {
    value: String,
    neighbors: Vec<Weak<RefCell<Node>>>,
}

impl Node {
    fn new(value: &str) -> Rc<RefCell<Node>> {
        Rc::new(RefCell::new(Node {
            value: value.to_string(),
            neighbors: Vec::new(),
        }))
    }

    fn add_neighbor(node: &Rc<RefCell<Node>>, neighbor: &Rc<RefCell<Node>>) {
        node.borrow_mut().neighbors.push(Rc::downgrade(neighbor));
    }
}

impl fmt::Debug for Node {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let neighbor_names: Vec<String> = self.neighbors.iter()
            .filter_map(|w| w.upgrade())
            .map(|n| n.borrow().value.clone())
            .collect();
        write!(f, "{} -> {:?}", self.value, neighbor_names)
    }
}

fn main() {
    let a = Node::new("A");
    let b = Node::new("B");
    let c = Node::new("C");
    let d = Node::new("D");

    Node::add_neighbor(&a, &b);
    Node::add_neighbor(&a, &c);
    Node::add_neighbor(&b, &c);
    Node::add_neighbor(&b, &d);
    Node::add_neighbor(&c, &d);

    println!("=== Graph Structure ===");
    for node in [&a, &b, &c, &d] {
        println!("{:?}", node.borrow());
    }

    println!("\n=== Nodes Reachable from Starting Point A ===");
    if let Some(neighbor) = a.borrow().neighbors.first().and_then(|w| w.upgrade()) {
        println!("A's first neighbor: {}", neighbor.borrow().value);
        let second_hop: Vec<String> = neighbor.borrow().neighbors.iter()
            .filter_map(|w| w.upgrade())
            .map(|n| n.borrow().value.clone())
            .collect();
        println!("Take it a step further from that neighbor: {:?}", second_hop);
    }

    println!("\n=== Strong reference counting ===");
    println!("A's Rc reference count: {}", Rc::strong_count(&a));
    let a_clone = Rc::clone(&a);
    println!("After clone, A's Rc reference count: {}", Rc::strong_count(&a));
    drop(a_clone);
    println!("After drop, A's Rc reference count: {}", Rc::strong_count(&a));
}

Resultado:

TEXT 📖 Somente leitura
=== Graph Structure ===
A -> ["B", "C"]
B -> ["C", "D"]
C -> ["D"]
D -> []

=== Nodes Reachable from Starting Point A ===
A's first neighbor: B
Take it a step further from that neighbor: ["C", "D"]

=== Strong reference counting ===
A's Rc reference count: 1
After clone, A's Rc reference count: 2
After drop, A's Rc reference count: 1

Os nós do gráfico utilizam Rc<RefCell<Node>> para implementar a propriedade compartilhada e a mutabilidade interna; as relações de vizinhança utilizam Weak<RefCell<Node>> para evitar vazamentos de memória causados por referências circulares. Rc::downgrade cria uma referência fraca, e weak.upgrade() tenta transformá-la em uma referência forte.


❓ Perguntas Frequentes

P: Qual é a diferença entre Box<T> e uma referência comum &T? R: Box<T> é proprietária dos dados, enquanto &T apenas os toma emprestado. Box<T> descarta os dados (libera memória do heap) quando sai do escopo, enquanto &T apenas os toma emprestados e não afeta o ciclo de vida dos dados. Box<T> garante que os dados residam no heap, e &T pode apontar tanto para a pilha quanto para o heap.

P: Qual é a diferença entre Rc<T> e Arc<T>? R: Rc<T> é uma contagem de referências em um único thread, enquanto Arc<T> é uma contagem de referências atômica em múltiplos threads. Rc utiliza operações de contagem não atômicas (para melhor desempenho) e não pode ser usado entre threads. Arc utiliza operações atômicas (com uma ligeira sobrecarga de desempenho) e pode ser usado entre threads. Os requisitos da característica Send determinam que Rc não pode ser usado entre threads.

P: Qual é a diferença entre RefCell<T> e Cell<T>? R: Cell<T> implementa a mutabilidade interna por meio da cópia (ou movimentação) de valores, enquanto RefCell<T> a implementa por meio de referências. Os métodos get/set de Cell<T> requerem T: Copy e são adequados para tipos pequenos. RefCell<T> pode ser usado com qualquer tipo; requer a obtenção de uma referência por meio de borrow/borrow_mut, e as regras de empréstimo são verificadas em tempo de execução.

P: Quando se deve usar RefCell<T> em vez de &mut T? R: Use RefCell quando você tiver uma referência imutável, mas precisar modificar os dados. Cenário comum: a assinatura de um método de trait aceita apenas &self, mas o método precisa modificar o estado internamente (por exemplo, caches, logs, contadores). RefCell adia a verificação de empréstimo do tempo de compilação para o tempo de execução.

P: Qual é a diferença de desempenho entre Rc<RefCell<T>> e &mut T? R: Rc<RefCell<T>> acarreta sobrecarga devido à contagem de referências e às verificações de empréstimo em tempo de execução, enquanto &mut T não apresenta sobrecarga. Cada chamada ao Rc::clone adiciona uma operação atômica (não atômica para execução em um único thread), e cada chamada ao borrow_mut realiza uma verificação em tempo de execução. No entanto, em comparação com a cópia profunda de dados, essas sobrecargas são insignificantes.


📖 Resumo


📝 Exercícios

  1. Dificuldade ⭐: Escreva um programa que use Box<T> para criar uma Cons List recursiva (contendo pelo menos 3 elementos) e, em seguida, imprima a lista inteira. Você deve usar #[derive(Debug)] para imprimir a lista.
  2. Dificuldade ⭐⭐: Implemente um sistema de “nota compartilhada”: use Rc<RefCell<String>> para permitir que três pessoas (Alice, Bob e Carol) compartilhem uma única sequência de caracteres. Todos podem lê-la e todos podem escrever nela (acrescentar conteúdo). Verifique se todos conseguem ver o conteúdo completo depois que todos tiverem terminado de escrever.
  3. Dificuldade ⭐⭐⭐: Implemente o padrão “Graph Observer”. Defina trait Observer { fn notify(&self, msg: &str); } e, em seguida, implemente LoggerObserver (que utiliza RefCell<Vec<String>> internamente para registrar mensagens). Em seguida, use Rc<RefCell<dyn Observer>> para permitir que vários observadores sejam notificados pelo mesmo sujeito. Na função main, crie dois observadores; faça com que o sujeito envie três mensagens e verifique se ambos os observadores receberam todas as mensagens.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%