Separação de Propriedades e Ações | Rust
- Separação de Propriedades e Ações | Rust
Modelo Estrutural no Rust
No Rust, a definição de dados e comportamento é separada explicitamente.
struct→ define propriedades (estado)impl→ define ações (comportamento)
Essa separação não é estética. Ela é estrutural e influencia:
- Layout de memória
- Mutabilidade
- Segurança
- Extensibilidade
Definição de Propriedades (struct)
A struct define apenas o estado.
struct User {
name: String,
email: String,
active: bool,
}
Características importantes:
- Define apenas campos
- Não contém comportamento
- Determina layout em memória
- Pode ser parcialmente pública (
pubpor campo)
Definição de Ações (impl)
O comportamento é definido em blocos impl.
impl User {
fn new(name: String, email: String) -> Self {
Self {
name,
email,
active: true,
}
}
fn deactivate(&mut self) {
self.active = false;
}
fn is_active(&self) -> bool {
self.active
}
}
Aspectos técnicos relevantes:
&self→ acesso imutável&mut self→ acesso mutávelself→ move ownership
Essa assinatura explicita o modelo de borrowing.
Açúcar Sintático de Métodos
Internamente, métodos são funções associadas ao tipo.
user.deactivate();
Equivale a:
User::deactivate(&mut user);
Isso demonstra que:
- Rust não possui métodos “intrínsecos”
- Métodos são funções com um primeiro parâmetro especial
Por Que Rust Separa Estado de Comportamento
1. Controle Explícito de Mutabilidade
Mutabilidade não é implícita.
fn deactivate(&mut self)
O compilador exige que:
- A variável seja mutável (
let mut user) - Não haja conflitos de borrow
Isso reduz:
- Side effects ocultos
- Condições de corrida
- Estados inconsistentes
2. Organização Modular
Rust permite múltiplos blocos impl.
impl User {
fn email_domain(&self) -> &str {
self.email.split('@').nth(1).unwrap()
}
}
impl User {
fn activate(&mut self) {
self.active = true;
}
}
Isso facilita:
- Organização por domínio
- Separação por módulos
- Extensões sem modificar código original
Onde o trait Entra
O trait define comportamento abstrato que pode ser implementado por múltiplos tipos.
Ele não pertence à struct.
Ele descreve um contrato.
Definição de Trait
trait Identifiable {
fn id(&self) -> String;
}
Implementação para um Tipo
impl Identifiable for User {
fn id(&self) -> String {
format!("{}:{}", self.name, self.email)
}
}
Aqui ocorre algo fundamental:
- O comportamento é separado do tipo
- O tipo pode implementar múltiplos traits
- Traits podem ser implementados externamente (respeitando coherence rules)
Traits vs Impl Direto
Impl Direto
impl User {
fn greet(&self) {
println!("Hello {}", self.name);
}
}
- Método específico do tipo
- Não é reutilizável via abstração
Trait
trait Greet {
fn greet(&self);
}
impl Greet for User {
fn greet(&self) {
println!("Hello {}", self.name);
}
}
Agora:
- Pode ser usado como bound genérico
- Permite polimorfismo estático
fn greet_user<T: Greet>(item: T) {
item.greet();
}
Relação com o Sistema de Tipos
Rust utiliza:
- Tipos algébricos
- Traits
- Monomorfização (em generics)
Separar propriedades de ações permite:
- Extensão por composição
- Polimorfismo via trait bounds
- APIs mais previsíveis
Comparação Conceitual com OOP Clássico
| Linguagens OO tradicionais | Rust |
|---|---|
| Classe agrupa tudo | Struct define apenas dados |
| Métodos fazem parte da classe | Métodos são funções associadas |
| Herança estrutural | Composição + Traits |
| Mutabilidade implícita | Mutabilidade explícita |
Rust evita herança estrutural e favorece:
- Composição
- Implementação de múltiplos contratos
- Segurança em tempo de compilação
Impacto em Concorrência
A separação reforça o modelo de:
- Ownership
- Borrow checker
SendeSync(traits de concorrência)
Como mutabilidade é explícita, o compilador consegue:
- Garantir ausência de data races
- Impedir aliasing mutável
- Validar acesso concorrente seguro
Fluxo Conceitual Resumido
struct→ define estadoimpl→ define comportamento específicotrait→ define contrato comportamentalimpl Trait for Type→ conecta contrato ao tipo
Essa arquitetura permite um modelo previsível, seguro e extensível.