Pular para o conteúdo principal
Módulo 6 · Lição 2 de 212/14 no curso~15 min
Lições do módulo (2/2)

Ponteiros inteligentes: Box e Rc

Um ponteiro inteligente é uma estrutura de dados que se comporta como um ponteiro, mas possui metadados e recursos extras (como contagem de referências ou gerenciamento automático de memória).

No Rust, referências comuns (como &) apenas emprestam dados. Os ponteiros inteligentes, por outro lado, geralmente possuem os dados para os quais apontam. Exemplos comuns incluem String e Vec<T>, mas a biblioteca padrão fornece outros especializados.


Box<T> para alocação de heap

O ponteiro inteligente mais simples é Box<T>, que armazena dados na heap em vez de na pilha. Somente o ponteiro para os dados alocados no heap permanece na pilha.

É usado principalmente nos seguintes casos:

  1. Quando você tem um tipo cujo tamanho não pode ser conhecido em tempo de compilação (como um tipo recursivo) e deseja usar um valor desse tipo em um contexto que requer um tamanho exato.
  2. Quando você deseja transferir a propriedade de uma grande quantidade de dados sem copiá-los.

Exemplo básico:

Rust
fn main() {
    let b = Box::new(5); // allocates the value 5 on the heap
    println!("b = {}", *b); // dereferences with * to read the value
}

Tipos recursivos e listas de contras

Um tipo recursivo é um tipo que pode conter um valor próprio como parte de sua definição. Rust deve saber o tamanho exato de um tipo em tempo de compilação. Como a profundidade de um tipo recursivo pode ser infinita, o compilador gera um erro a menos que você use um Box (que tem tamanho fixo, pois é um ponteiro):

Rust
enum List {
    Cons(i32, Box<List>),
    Nil,
}

Rc<T>: Contagem de Referência

Há casos em que um único valor pode ter vários proprietários. O ponteiro inteligente Rc<T> (referência contada) monitora o número de referências a um valor para determinar se o valor ainda está em uso. Se a contagem cair para zero, o recurso será desalocado com segurança.

[!NOTA] Rc<T> é adequado apenas para cenários de thread único. Para cenários multithread (simultâneos), você deve usar Arc<T> (Referência Atômica Contada).

Rust
use std::rc::Rc;

fn main() {
    let a = Rc::new(5);
    let b = Rc::clone(&a); // increments the reference count to 2
    println!("References: {}", Rc::strong_count(&a));
}

Experimente você mesmo

Exercício 1: Alocação de Heap com Caixa

Exercício#rust.m6.l2.e1
Tentativas: 0Carregando…

Aloque um valor inteiro de 42 no heap usando Box::new e atribua-o a uma variável chamada val. Em seguida, imprima o valor armazenado na Caixa desreferenciando-o explicitamente com o operador *.

Carregando editor…
Mostrar dica

Use `let val = Box::new(42);` para armazenar o valor no heap e desreferencia-lo com `*val` dentro de `println!`.

Solução disponível após 3 tentativas

Exercício 2: Estrutura de dados recursiva (lista de contras)

Exercício#rust.m6.l2.e2
Tentativas: 0Carregando…

Defina uma enumeração recursiva chamada List contendo duas variantes: Cons que envolve uma tupla (i32, Box<List>) e Nil que representa o fim da lista. Na função principal, instancie uma lista vinculada contendo o elemento 1 seguido de Nil.

Carregando editor…
Mostrar dica

A enum é declarada como `enum List {Cons(i32, Box<List>), Nil }`. Instancie a lista com `List::Cons(1, Box::new(List::Nil))`.

Solução disponível após 3 tentativas

Exercício 3: Compartilhando Propriedade com Rc

Exercício#rust.m6.l2.e3
Tentativas: 0Carregando…

Use std::rc::Rc para criar um número inteiro compartilhado contendo o valor 100 em uma variável a. Clone a referência em uma variável b usando Rc::clone. Por fim, imprima a contagem de referências ativas usando a função Rc::strong_count.

Carregando editor…
Mostrar dica

Crie o valor com `Rc::new(100)`. Clone com `Rc::clone(&a)`e exiba a contagem de proprietários com`Rc::strong_count(&a)`.

Solução disponível após 3 tentativas