Uma dos maiores diferenciais da linguagem Rust é sua proposta para gerenciamento de memória mais seguro e eficiente, sem o uso de Garbage collector
e feito diretamente pelo compilador. Há maiores vantagens dessa linguagem, porém esse fato já permite menor ocorrência de erros relacionados a gerenciamento de memória, um dos mais presentes em sistemas de informação, segundo a Microsoft, e com alto potencial destrutivo.
Ownership e Borrowing.
O Rust alcança esse nível de segurança em relação a memória através de duas técnicas relativamente simples: Ownership
e Borrowing
.
Muito resumidamente, Ownership
é usada pelo compilador para dar "pose" de uma valor a um contexto, permitindo rastrear seu escopo, início e fim de uso, liberando a memória ocupada. Já o Borrowing
é a técnica de "emprestar" essa "pose" a outro novo contexto, na qual invalida a pose anterior; tudo isso em tempo de compilação.
No vídeo abaixo, essa técnica e exemplificada de maneira bem lúdica
- O valor é movido a um novo contexto, o qual ganha posse desse e fica responsável pela liberação da memória, invalidando a posse anterior.
- O valor é emprestado de modo imutável para o novo contexto, permitindo apenas a leitura deste, mantendo a pose e a responsabilidade pela liberação de memória ao contexto que empresta. Pode haver várias referências imutáveis.
- Balão é emprestado de modo mutável, permitindo o novo contexto modificar esse valor, mas ainda mantendo a pose e a responsabilidade pela liberação de memória ao contexto que empresta. Apenas pode haver uma referência mutável.
Stack e a Heap
Ambos os termos também tem relação com o gerenciamento de memória, mais especificamente com o armazenamento da memória da aplicação. O Stack
é um espaço limitado e delimitado pelo compilador no qual dados do contexto atual com tamanho conhecidos e ponteiros para Heap
são armazenados, já a Heap
, basicamente, armazena dados com tamanho desconhecidos em tempo de compilação, mas conhecidos em tempo de execução, e é a partir dai que o tema da postagem ganha importância.
Imagine a seguinte situação: você está trabalhando num crate para fazer a leitura de arquivos que mudam de tamanho constantemente em tempo de execução, o que fazer? Para essa situação, um Smart pointer
*chamado *Box, que tem um tipo fixo na Stack
, **poderia ser utilizado. Esse tipo embrulha uma estrutura de tamanho variável e aponta para a Heap
, permitindo a alteração do tamanho ocupado.
Claro, tudo está bem resumido. Para informações mais precisas, consulte a documentação do Rust.
Trait
"Uma Trait é uma coleção de métodos definidos para um tipo desconhecido, podendo acessar outros métodos definidos no mesmo Trait"
Sabendo que estou tentando comparar o Rust com um linguagem de programação OO tradicional, é possível dizer que os Traits
são uma forma de compartilhar comportamentos abstratos entre tipos de dados, como uma interface
.
Um princípio recomendado na orientação orientada a objetos é a inversão de dependência, o qual diz que o código não deve depender de um implementação, mas sim de uma abstração. Isso significa que, quando se necessita que um objeto herde certo comportamento, mas não se sabe qual classe das que herdam tal comportamento se quer, se usa o tipo do qual os objetos herdam. Há algo bem-parecido com o princípio de inversão de dependência no Rust utilizando Traits
.
Finalmente... o que é dyn?
O uso prática dessa palavra reservada é até simples: o dyn
é utilizado para indicar que o tipo de uma certa estrutura precisa ser implementação de um certo Trait
de maneira dinâmica, em tempo de execução.
trait Service {
fn do(&self);
fn get(&self) {
//...
}
}
struct ServiceA {
//...
}
impl Service for ServiceA {
fn do(&self) {
//...
}
}
fn main() {
// Em versões anteriores do Rust, não era necessário o uso do 'dyn'.
// O que esse 'statemente' diz, basicamente, é:
// A definição 'sevice' precisa ser de um tipo que implementa o Trait
// Service.
let service: Box<dyn Service> = Box::new(ServiceA::new());
//...
}
Como o tamanho dos Traits
não são conhecidos em tempo de compilação, se faz necessário que esses Traits
sejam envoltos em um Smart Point
, que no caso do exemplo acima é um Box, ou os usados como referências com &
.
Sendo um pouco mais técnico, a palavra reservada dyn
faz referência ao dispatch
dinâmico, já o impl
, um outra palavra reservado do Rust, também usada para definir herança de tipos, para o dispatch
estático.
Dispatch dinâmico ou estático?
Há uma pequena questão que é preciso esclarecer sobre o tema: o que são dispatch
dinâmico e estático? O dispatch
dinâmico é um processo de selecionar qual implementação chamar em tempo de execução, já o dispatch
estático é uma forma de polimorfismo de código que ocorre totalmente em tempo de compilação. Esse dispatch
descreve como uma linguagem de programão seleciona qual implementação de um método irá ser utilizado.
"O static dispatch vai gerar mais código, tipo generics do java, e compilar tudo estaticamente, enquanto dynamic dispatch acontece em tempo de execução e não gera esses códigos, portanto, sendo menos performático que o static dispatch". @jcbritogardona no Twitter, editado.
Para cada structure
que implementa um certo Trait
com impl
, dispatch
estático, o compilador irá gerar código de substituição pras todas essas estruturas durante a compilação, já quando o dyn
é utilizado, o dispatch
dinâmico fica responsável por gerar esses códigos durante execução do código.
Conclusão
O uso do dyn
é relativamente simples, mas pode assustar novos programadores por parecer introduzir um provável nível de complexidade desnecessário; já que essa abordagem não existe em outras linguagens de programação tão no controle do desenvolvedor.
Confesso que o tema dispatch
ainda não sobre meu total domínio, mas como sempre que tenho dificuldade de entender um tema nos meus estudos, irei buscar entender e criar um pequeno resumo para tentar ajudar alguém.
Agradeço pela leitura e espero que tenha ajudado. Fique à vontade para comentar, compartilhar ou corrigir algo; eu vou agradecer bastante.
Referências
- http://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/book/first-edition/the-stack-and-the-heap.html
- https://doc.rust-lang.org/book/ch10-02-traits.html
- https://doc.rust-lang.org/std/keyword.dyn.html
- https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/dyn-trait-for-trait-objects.html
- https://www.educative.io/edpresso/what-is-the-dyn-keyword-in-rust
- https://en.wikipedia.org/wiki/Dynamic_dispatch
- https://en.wikipedia.org/wiki/Static_dispatch
- https://medium.com/ingeniouslysimple/static-and-dynamic-dispatch-324d3dc890a3
- https://doc.rust-lang.org/std/keyword.impl.html
- https://doc.rust-lang.org/rust-by-example/generics/impl.html
Top comments (0)