Box<T>
O Smart Pointer
Boxheap
e também implementa as traits Deref
e DerefMut
, sendo assim, é possível apontar para um endereço de memória na Heap e realizar modificações nesta região de memória. Vamos a um exemplo mais simples.
fn main() {
let numero: Box<usize> = Box::new(42);
println!("{}", numero);
}
Neste exemplo acima, apontamos para uma região da heap
, um número inteiro sem sinal, o Box, obedece às mesmas regras de ownership que ponteiros comuns.
Como ele foi declarado como imutável, não podemos fazer o DerefMut
e alterar o valor na heap, caso tentarmos fazer isso teremos o seguinte erro.
fn main() {
let numero: Box<usize> = Box::new(42);
*numero = 69;
println!("{}", numero);
}
error[E0594]: cannot assign to `*numero`, as `numero` is not declared as mutable
--> boxx.rs:4:5
|
3 | let numero = Box::new(1);
| ------ help: consider changing this to be mutable: `mut numero`
4 | *numero = 69;
| ^^^^^^^^^^^^ cannot assign
error: aborting due to previous error
For more information about this error, try `rustc --explain E0594`.
Declarando a variável como mutável podemos realizar essa alteração.
fn main() {
let mut numero: Box<usize> = Box::new(42);
*numero = 69;
println!("{}", numero);
}
Tipos recursivos
Tipos recursivos, são aqueles que tem algum atributo que é representa ele mesmo. Como, por exemplo, em uma árvore binaria, cada "galho" é um tipo recursivo.
struct Galho {
esquerda: Option<Galho>,
direita: Option<Galho>,
valor: usize,
}
fn main() {
todo!();
}
Caso tentarmos compilar o código acima teremos o seguinte output do compilador:
error[E0072]: recursive type `Galho` has infinite size
--> recursive.rs:2:1
|
2 | struct Node {
| ^^^^^^^^^^^ recursive type has infinite size
3 | esquerda: Option<Galho>,
| ------------ recursive without indirection
4 | direita: Option<Galho>,
| ------------ recursive without indirection
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Node` representable
|
3 ~ esquerda: Option<Box<Galho>>,
4 ~ direita: Option<Box<Galho>>,
|
error: aborting due to previous error
For more information about this error, try `rustc --explain E0072`.
O próprio compilador já nos diz algo que podemos fazer para corrigir este problema, mas antes vamos entender o que seria essa mensagem aqui "recursive type has infinite size".
Teoricamente tipos recursivos podem crescer ao infinito e o compilador do Rust não sabe o quanto de memória sera necessário para esse tipo, por isso essa mensagem. Porém, ele sabe exatamente quanta memória é necessária para um Box
, sendo possível a criação deste tipo recursivo utilizando esse Box
.
struct Galho {
esquerda: Option<Box<Galho>>,
direita: Option<Box<Galho>>,
valor: usize,
}
fn main() {
todo!();
}
No livro Rust Book é possível ver um exemplo bem interessante utilizando uma Cons List.
Valores Dinâmicos
Recuperando o exemplo das traits de animais do capítulo sobre traits
Vamos supor que queremos ter uma lista de animais, sendo eles do tipo Cachorro, Gato e Papagaio. Podemos utilizar a palavra reservada dyn
dentro de um Box<T>
, para representar valores dinâmicos.
trait Animal {
fn comer(&self);
}
struct Cachorro {}
impl Animal for Cachorro {
fn comer(&self) {
println!("Cachorro comendo...");
}
}
struct Gato {}
impl Animal for Gato {
fn comer(&self) {
println!("gato comendo...");
}
}
struct Papagaio {}
impl Animal for Papagaio {
fn comer(&self) {
println!("papagaio comendo...");
}
}
fn main() {
let animais: Vec<Box<dyn Animal>> = vec![
Box::new(Gato {}),
Box::new(Cachorro {}),
Box::new(Papagaio {}),
];
for animal in animais {
animal.comer();
}
}
Utilizando valores dinâmicos podemos ter tipos diferentes que implementam a mesma trait. Só devemos saber que, um ponteiro dinâmico ocupa o dobro do espaço que um ponteiro comum, isso por que temos um ponteiro para o tipo em questão e outro ponteiro para a implementação desta trait
.