Skip to main content

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:

  • b passa a ser o owner
  • a torna-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:

  • u64
  • i32
  • bool
  • char
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:

  • s continua 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:

  • s precisa ser mut
  • 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