C te dá controle total — e uma arma apontada pro próprio pé. Go resolve com Garbage Collector — e picos de latência imprevisíveis. O Rust encontrou um terceiro caminho: segurança de memória garantida pelo compilador, sem custo em runtime.

Toda linguagem precisa responder a uma pergunta fundamental: quando a memória que um valor ocupava pode ser liberada? A resposta define tudo — performance, segurança, previsibilidade.
Você aloca e libera. Máxima performance, máxima responsabilidade. Esqueceu um free(): memory leak. Liberou duas vezes: double-free. Acessou depois de liberar: use-after-free. 70% das vulnerabilidades da Microsoft vieram daqui.
O runtime decide. Você não gerencia memória — o GC varre e libera o que não está mais em uso. Seguro, mas com custo: pausas periódicas imprevisíveis. O Discord migrou porque o GC causava picos a cada 2 minutos.
O compilador decide. Regras verificadas em tempo de compilação garantem que a memória seja liberada exatamente quando deve — sem GC, sem gerenciamento manual, sem surpresas em runtime.
O sistema de ownership do Rust tem três regras simples. Toda a segurança de memória da linguagem deriva delas.
Uma variável é a dona do valor que ela armazena. Não existe valor sem dono.
Quando você atribui um valor a outra variável, a propriedade se transfere — a variável original deixa de ser válida.
Sem GC, sem free(). O compilador insere a liberação automaticamente quando a variável deixa de existir.
Na prática, isso fica assim:
let s1 = String::from("blueprint");let s2 = s1; // ownership se transfere pra s2println!("{}", s1); // ❌ erro de compilação: s1 não é mais válidaprintln!("{}", s2); // ✅ s2 é a nova donaEm C ou Go, esse código funcionaria — e você teria dois ponteiros apontando pro mesmo dado na memória. Em Rust, o compilador recusa na hora. Não existe chance de double-free ou uso acidental de um valor já transferido.
Se ownership fosse a única forma de acessar dados, você teria que transferir a propriedade toda vez que passasse algo pra uma função — e receber de volta depois. Inviável.
É aí que entra o borrowing: você empresta uma referência ao valor sem transferir o ownership.
fn tamanho(s: &String) -> usize { // recebe uma referência, não o valor s.len()}let minha_string = String::from("blueprint");let tam = tamanho(&minha_string); // passa a referênciaprintln!("{} tem {} caracteres", minha_string, tam);// ✅ minha_string ainda é válida — ownership não foi transferidaVocê pode emprestar mutabilidade também — mas com uma restrição crucial:
let mut s = String::from("blueprint");let r1 = &mut s;let r2 = &mut s; // ❌ erro: não pode ter duas referências mutáveis ao mesmo tempolet mut s = String::from("blueprint");{ let r1 = &mut s; r1.push_str(" blog");} // r1 sai de escopo aquilet r2 = &mut s; // ✅ agora pode — r1 já não existeEssa regra elimina data races em tempo de compilação. Em Go ou C, dois threads podendo escrever no mesmo dado ao mesmo tempo é uma bomba-relógio. Em Rust, o compilador simplesmente não deixa isso acontecer.
O compilador do Rust consegue inferir a maioria das situações de ownership e borrowing automaticamente. Mas existe um caso onde ele precisa de uma dica explícita: quando uma função retorna uma referência, e o compilador precisa saber de qual dos parâmetros ela vem.
// Sem lifetime — o compilador não sabe de onde vem a referência retornadafn maior(x: &str, y: &str) -> &str { // ❌ erro de compilação if x.len() > y.len() { x } else { y }}// Com lifetime — o compilador sabe que o retorno vive tanto quanto x e yfn maior<'a>(x: &'a str, y: &'a str) -> &'a str { // ✅ if x.len() > y.len() { x } else { y }}O 'a é uma anotação de lifetime. Ela não cria nada — só diz ao compilador: "a referência retornada vive pelo mesmo tempo que os dois parâmetros." Com essa informação, ele consegue verificar que você nunca vai retornar uma referência pra algo que já foi liberado.
Use-after-free é uma das vulnerabilidades mais comuns e perigosas. Veja como cada linguagem lida com ela:
/* C — compila, executa, comportamento indefinido */char *s = malloc(10);free(s);printf("%s", s); // acessa memória liberada — undefined behavior// Go — GC previne liberação prematura, mas com custo em runtime// O GC garante que s não será liberada enquanto ainda houver referênciass := "blueprint"fmt.Println(s) // sempre seguro, mas o GC adiciona overhead// Rust — erro em tempo de compilação, zero custo em runtimelet s = String::from("blueprint");drop(s); // libera explicitamenteprintln!("{}", s); // ❌ erro de compilação — o compilador recusa// esse código nunca chega em produçãoEssa é a diferença fundamental: C descobre o problema em produção (quando já causou dano). Go previne em runtime com GC (mas paga o custo de latência). Rust previne em compile time — sem nenhum custo em runtime.