Ownership e Borrowing | Rust
- Ownership e Borrowing | Rust
Contexto
Segundo Programming Rust (Capítulos 4 e 5), o sistema de ownership, moves e borrows é o mecanismo central que permite ao Rust ser memory-safe sem garbage collector .
O compilador aplica essas regras em tempo de compilação, eliminando:
- use-after-free
- double free
- data races
- dangling references
Esse modelo substitui tanto o GC (Java, C#) quanto o gerenciamento manual (C/C++).
Ownership
Ownership define quem é responsável pela memória de um valor.
Regras Fundamentais
- Cada valor possui um único owner
- Quando o owner sai de escopo, o valor é desalocado
- Ownership pode ser transferido (move)
Exemplo de Escopo
fn main() {
let s = String::from("hello");
} // s sai de escopo → memória liberada automaticamente
A liberação ocorre automaticamente via Drop.
Move Semantics
Tipos que alocam memória no heap (ex: String, Vec) não implementam Copy por padrão.
fn main() {
let a = String::from("Rust");
let b = a; // move
// println!("{}", a); // ERRO: ownership foi movido
}
Após o move:
bpassa a ser o owneratorna-se inválido
Clone explícito
let a = String::from("Rust");
let b = a.clone(); // cópia profunda
Copy Types (Exceção)
Tipos primitivos implementam Copy:
u64i32boolchar
let x = 10;
let y = x; // cópia implícita
println!("{}", x); // válido
Esses valores vivem na stack e não possuem desalocação complexa.
Borrowing
Borrowing permite usar um valor sem transferir ownership.
É feito por referência:
&T→ referência imutável&mut T→ referência mutável
Empréstimo Imutável
fn main() {
let s = String::from("hello");
print_len(&s);
println!("{}", s); // ainda válido
}
fn print_len(text: &String) {
println!("{}", text.len());
}
Aqui:
scontinua sendo o owner- a função apenas empresta a referência
Regras de Borrowing
O compilador garante:
- Pode haver múltiplas referências imutáveis (
&T) - OU uma única referência mutável (
&mut T) - Nunca ambas ao mesmo tempo
Válido
let s = String::from("Rust");
let r1 = &s;
let r2 = &s;
println!("{}, {}", r1, r2);
Inválido
let mut s = String::from("Rust");
let r1 = &s;
let r2 = &mut s; // ERRO
Essa regra elimina data races em tempo de compilação.
Borrowing Mutável
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s);
}
fn change(text: &mut String) {
text.push_str(" world");
}
Regras importantes:
sprecisa sermut- Só pode existir uma referência mutável ativa
Lifetimes
Borrowing depende de lifetime checking.
Uma referência:
- nunca pode viver mais do que o valor referenciado
Exemplo inválido:
fn dangle() -> &String {
let s = String::from("hello");
&s
}
O valor é destruído ao sair do escopo, então retornar a referência é proibido.
Modelo Mental
Você pode pensar assim:
- Ownership → quem destrói o valor
- Move → transferência de responsabilidade
- Borrow → acesso temporário controlado
- Lifetime → duração válida da referência
Esse modelo permite:
- Segurança sem GC
- Previsibilidade de performance
- Concorrência segura