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:
- 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.
- Quando você deseja transferir a propriedade de uma grande quantidade de dados sem copiá-los.
Exemplo básico:
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):
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 usarArc<T>(Referência Atômica Contada).
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
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 *.
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)
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.
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
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.
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