pratica

Métodos e Funções Associadas em Structs

Aprenda sobre métodos e funções associadas em structs

25 min
Aula 2 de 5

Métodos e Funções Associadas em Structs

Olá, estudantes! 👋 Sejam bem-vindos a mais uma aula do nosso curso de Rust. Hoje, vamos mergulhar em um conceito fundamental para a organização e encapsulamento de lógica em Rust: Métodos e Funções Associadas em Structs.

Este tópico é crucial para construir programas robustos e bem estruturados, permitindo que suas structs não sejam apenas contêineres de dados, mas também possuam comportamentos definidos. Pense nisso como dar "ações" e "habilidades" às suas estruturas de dados! 🚀

1. Introdução: Dando Vida às Suas Structs

Até agora, aprendemos a criar structs para agrupar dados relacionados. No entanto, o verdadeiro poder da programação orientada a objetos (ou, no caso de Rust, uma abordagem que permite a programação orientada a objetos) vem da capacidade de associar comportamento a esses dados. É aqui que entram os métodos e as funções associadas.

  • Métodos: São funções que pertencem a uma struct e são chamadas em uma instância específica dessa struct. Eles geralmente operam nos dados dessa instância.
  • Funções Associadas: Também pertencem a uma struct, mas são chamadas na própria struct, não em uma instância específica. Pense nelas como "funções estáticas" em outras linguagens, frequentemente usadas para criar novas instâncias (construtores) ou para operações que não dependem de um estado específico da instância.

Vamos explorar cada um deles com exemplos práticos!

2. Explicação Detalhada com Exemplos

2.1. Métodos: Comportamento de Instância 🧍‍♀️➡️🏃‍♀️

Métodos são definidos dentro de um bloco impl (de "implementation") para uma struct. O primeiro parâmetro de um método é sempre self, que representa a instância da struct na qual o método está sendo chamado.

Existem algumas variações para self:

  • &self: A forma mais comum. Permite que o método leia os dados da instância sem tomar posse dela ou modificá-la. É como passar uma referência imutável.
  • &mut self: Permite que o método leia e modifique os dados da instância. É como passar uma referência mutável.
  • self: O método toma posse da instância. A instância original não pode ser usada após a chamada do método. Isso é menos comum, mas útil para transformações que consomem a instância original (ex: into_iter()).

Exemplo: Calculando a Área de um Retângulo

Vamos criar uma struct Retangulo e adicionar um método area que calcula sua área.

// Definição da struct
struct Retangulo {
    largura: u32,
    altura: u32,
}
 
// Bloco de implementação para Retangulo
impl Retangulo {
    // Um método que calcula a área do retângulo
    // &self indica que o método pega uma referência imutável à instância
    fn area(&self) -> u32 {
        self.largura * self.altura
    }
 
    // Um método que verifica se um retângulo pode conter outro
    fn pode_conter(&self, outro: &Retangulo) -> bool {
        self.largura > outro.largura && self.altura > outro.altura
    }
 
    // Um método que modifica a largura do retângulo (requer &mut self)
    fn escalar(&mut self, fator: u32) {
        self.largura *= fator;
        self.altura *= fator;
    }
}
 
fn main() {
    let ret1 = Retangulo {
        largura: 30,
        altura: 50,
    };
 
    let ret2 = Retangulo {
        largura: 10,
        altura: 40,
    };
 
    let ret3 = Retangulo {
        largura: 60,
        altura: 45,
    };
 
    println!("A área de ret1 é {} pixels quadrados.", ret1.area()); // Chamada de método
 
    println!("ret1 pode conter ret2? {}", ret1.pode_conter(&ret2));
    println!("ret1 pode conter ret3? {}", ret1.pode_conter(&ret3));
 
    let mut ret_mutavel = Retangulo {
        largura: 10,
        altura: 20,
    };
    println!(
        "Retângulo mutável antes de escalar: Largura={}, Altura={}",
        ret_mutavel.largura, ret_mutavel.altura
    );
    ret_mutavel.escalar(2); // Chamada de método que modifica a instância
    println!(
        "Retângulo mutável depois de escalar: Largura={}, Altura={}",
        ret_mutavel.largura, ret_mutavel.altura
    );
}

Neste exemplo:

  • area(&self): Acessa self.largura e self.altura para calcular a área, mas não modifica a instância.
  • pode_conter(&self, outro: &Retangulo): Compara as dimensões de self com as de outro, sem modificá-los.
  • escalar(&mut self, fator: u32): Modifica self.largura e self.altura, por isso precisa de &mut self.

2.2. Funções Associadas: Comportamento da Struct 🏗️➡️⚙️

Funções associadas são definidas também dentro de um bloco impl, mas não recebem self como primeiro parâmetro. Elas são chamadas usando a sintaxe StructName::function_name().

São frequentemente usadas como:

  • Construtores: Para criar novas instâncias da struct de uma maneira controlada (ex: new).
  • Funções utilitárias: Que operam em dados da struct ou produzem novas instâncias sem depender de uma instância existente.

Exemplo: Criando Retângulos

Vamos adicionar uma função associada new para criar instâncias de Retangulo e outra quadrado para criar um retângulo com lados iguais.

// Bloco de implementação para Retangulo (continuando o exemplo anterior)
impl Retangulo {
    // ... métodos area, pode_conter, escalar ...
 
    // Uma função associada (construtor) para criar um novo Retangulo
    // Não recebe `self`
    fn new(largura: u32, altura: u32) -> Retangulo {
        Retangulo { largura, altura }
    }
 
    // Uma função associada para criar um quadrado (um tipo especial de retângulo)
    fn quadrado(tamanho: u32) -> Retangulo {
        Retangulo {
            largura: tamanho,
            altura: tamanho,
        }
    }
}
 
fn main() {
    // ... código anterior ...
 
    // Usando a função associada `new`
    let ret_novo = Retangulo::new(25, 40); // Chamada de função associada
    println!(
        "Um novo retângulo criado com 'new' tem área: {}",
        ret_novo.area()
    );
 
    // Usando a função associada `quadrado`
    let quad = Retangulo::quadrado(15);
    println!("Um quadrado de 15x15 tem área: {}", quad.area());
}

Neste exemplo:

  • Retangulo::new(largura, altura): É uma convenção em Rust usar new para funções associadas que atuam como construtores. Ela retorna uma nova instância de Retangulo.
  • Retangulo::quadrado(tamanho): Uma função associada que encapsula a lógica de criar um retângulo com largura e altura iguais.

3. Código de Exemplo Oficial (Adaptado do The Rust Programming Language) 📚

A documentação oficial do Rust (The Rust Programming Language Book) utiliza um exemplo similar de Rectangle para ilustrar métodos. Vamos reforçar esses conceitos com um trecho adaptado de lá.

// Exemplo da documentação oficial (adaptado)
#[derive(Debug)] // Adicionamos Debug para poder imprimir a struct facilmente
struct Rectangle {
    width: u32,
    height: u32,
}
 
impl Rectangle {
    // Método para calcular a área
    fn area(&self) -> u32 {
        self.width * self.height
    }
 
    // Método para verificar se um retângulo pode conter outro
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
 
    // Função associada para criar um quadrado
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}
 
fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };
 
    println!("O rect1 é {:?}", rect1); // Usando o derive(Debug)
 
    println!(
        "A área do rect1 é {} pixels quadrados.",
        rect1.area() // Chamada de método
    );
 
    println!("O rect1 pode conter o rect2? {}", rect1.can_hold(&rect2));
    println!("O rect1 pode conter o rect3? {}", rect1.can_hold(&rect3));
 
    let sq = Rectangle::square(25); // Chamada de função associada
    println!("Um quadrado de 25x25 tem área: {}", sq.area());
}

Observe que a sintaxe é idêntica à que usamos, reforçando que você está aprendendo a maneira idiomática de Rust! A macro #[derive(Debug)] é um atalho útil para permitir que imprimamos a struct usando {:?} para depuração.

4. Integração com Múltiplas Tecnologias (Não Aplicável Aqui)

Nesta aula, estamos focando em um conceito fundamental da linguagem Rust: métodos e funções associadas em structs. Este tópico é puramente sobre a sintaxe e a organização do código dentro do Rust, e não envolve a integração com outras bibliotecas ou frameworks externos. Portanto, não há uma seção de "integração de tecnologias" para este conteúdo específico. 😉

5. Exercícios/Desafios Práticos 💪

Agora é a sua vez de colocar a mão na massa! Crie uma struct Usuario e implemente métodos e funções associadas para gerenciar seus dados.

Desafio: Gerenciamento de Usuários

Crie uma struct chamada Usuario com os seguintes campos:

  • nome_de_usuario: String
  • email: String
  • ativo: bool (indica se a conta está ativa)
  • contador_logins: u64

Sua tarefa é:

  • 1. Implementar um Construtor (new): Crie uma função associada Usuario::new(nome_de_usuario: String, email: String) que retorne uma nova instância de Usuario. Por padrão, ativo deve ser true e contador_logins deve ser 1.
  • 2. Implementar um Método esta_ativo: Crie um método &self.esta_ativo() que retorne true se o usuário estiver ativo, e false caso contrário.
  • 3. Implementar um Método alterar_email: Crie um método &mut self.alterar_email(novo_email: String) que atualize o campo email do usuário.
  • 4. Implementar um Método registrar_login: Crie um método &mut self.registrar_login() que incremente o contador_logins do usuário em 1.
  • 5. Implementar uma Função Associada criar_convidado: Crie uma função associada Usuario::criar_convidado() que retorne uma nova instância de Usuario com os seguintes valores padrão:
  • 6. Testar no main: No seu bloco main, crie algumas instâncias de Usuario usando new e criar_convidado, chame todos os métodos e funções associadas implementadas e imprima os resultados para verificar se tudo funciona como esperado. Use #[derive(Debug)] para facilitar a impressão das structs.
// Comece seu código aqui!
#[derive(Debug)]
struct Usuario {
    // ... seus campos aqui
}
 
impl Usuario {
    // 1. Construtor new
    // 2. Método esta_ativo
    // 3. Método alterar_email
    // 4. Método registrar_login
    // 5. Função associada criar_convidado
}
 
fn main() {
    // 6. Teste suas implementações aqui
}

6. Resumo e Próximos Passos

Parabéns! 🎉 Você dominou os conceitos de métodos e funções associadas em structs.

  • Métodos (&self, &mut self, self) permitem que as instâncias das suas structs tenham comportamento e interajam com seus próprios dados.
  • Funções Associadas (StructName::function()) fornecem funcionalidades relacionadas à struct em si, como construtores ou utilitários.

Essas ferramentas são essenciais para organizar seu código de forma lógica e modular, tornando suas structs mais poderosas e expressivas.

No próximo módulo, vamos dar um passo adiante e explorar os Traits, que nos permitem definir comportamentos compartilhados entre diferentes tipos, abrindo as portas para um polimorfismo mais flexível em Rust. Prepare-se para um conceito ainda mais elegante! 😉

Até a próxima!

© 2025 Escola All Dev. Todos os direitos reservados.

Métodos e Funções Associadas em Structs - Curso gratuito de Rust: A linguagem mais amada | escola.all.dev.br