self vs Self em Rust: Guia completo para iniciantes e além

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Uma letra minúscula, uma grande diferença

\n\n

Olá, futuro desenvolvedor Rust! Se você já começou a estudar Rust, certamente notou que o compilador é um professor rigoroso, mas justo. Ele não perdoa erros de digitação, muito menos confusão na sintaxe. Uma das armadilhas mais comuns em que iniciantes (e não só) caem é a confusão entre self e Self. Aparentemente, a diferença é apenas uma letra — maiúscula ou minúscula. Mas por trás dessa letra esconde-se uma diferença fundamental entre a instância e o tipo.

\n\n

No Stack Overflow, essa pergunta tem milhares de visualizações, e não é por acaso. Hoje vamos entender de uma vez por todas a diferença entre self e Self em Rust. Não vamos apenas ler uma definição chata, mas sim seguir um tutorial passo a passo com exemplos práticos, escrevendo alguns pequenos programas. Ao final do artigo, você não apenas saberá a diferença, mas a sentirá em um nível intuitivo. Vamos lá!

\n\n

1. self: "Eu" — é sobre a instância

\n\n

Vamos começar com self. Em Rust, como em muitas outras linguagens, self é usado para se referir à instância atual de uma struct ou enumeração. É o análogo de this em C++, Java, JavaScript ou Python. Quando você escreve um método que é chamado em um objeto específico, você usa self.

\n\n

Existem três formas principais de self na assinatura do método:

\n
    \n
  • self — assume a posse da instância (consome a instância). Após a chamada do método, o objeto original fica inacessível.
  • \n
  • &self — uma referência imutável à instância (o caso mais comum). O método pode ler os dados, mas não pode alterá-los.
  • \n
  • &mut self — uma referência mutável à instância. O método pode alterar os campos da struct.
  • \n
\n\n

Vamos escrever um exemplo simples. Imagine que estamos criando uma struct Estudante.

\n\n
struct Estudante {\n    nome: String,\n    curso: u32,\n}\n\nimpl Estudante {\n    // Método que recebe &self (referência imutável)\n    fn apresentar(&self) {\n        println!("Olá! Eu sou {}, estou no {}º ano.", self.nome, self.curso);\n    }\n\n    // Método que recebe &mut self (referência mutável)\n    fn avancar_para_proximo_ano(&mut self) {\n        self.curso += 1;\n        println!("{} agora está no {}º ano!", self.nome, self.curso);\n    }\n\n    // Método que recebe self (posse)\n    fn concluir_estudos(self) {\n        println!("{} concluiu os estudos no {}º ano. Adeus!", self.nome, self.curso);\n        // self é destruído aqui, a memória é liberada\n    }\n}\n\nfn main() {\n    let mut estudante = Estudante {\n        nome: String::from("Ana"),\n        curso: 2,\n    };\n\n    estudante.apresentar(); // &self\n    estudante.avancar_para_proximo_ano(); // &mut self\n    estudante.concluir_estudos(); // self (posse)\n\n    // Após chamar concluir_estudos, a variável 'estudante' não está mais disponível!\n    // Descomente a linha a seguir para ver o erro de compilação:\n    // estudante.apresentar(); // ERRO: borrow of moved value\n}
\n\n

Observe o último comentário. Quando passamos self (sem referência), estamos transferindo a posse. Esta é uma ferramenta poderosa que permite controlar precisamente o tempo de vida do objeto. Em outras linguagens (por exemplo, Python), não há esse controle — o objeto vive enquanto houver referências a ele. Em Rust, você decide quando o objeto será "consumido".

\n\n

2. Self: "Eu" — é sobre o tipo

\n\n

Agora a parte mais interessante. Self (com letra maiúscula) é um apelido (alias) para o tipo para o qual estamos implementando métodos. Simplificando, Self dentro de um bloco impl é substituído pelo nome da struct ou enumeração com a qual estamos trabalhando.

\n\n

Por que isso é necessário? Primeiro, é conveniente. Em vez de escrever o nome completo da struct, você escreve Self. Segundo, é crucial

Blogs

Book Recommendations