Da trilha de Rust Este conceito ainda não saiu como card do dia. ir para o card de hoje

Rust Tipos Intermediário

Traits

Uma trait em Rust descreve um conjunto de comportamentos que um tipo pode implementar. Ela é parecida com uma interface de outras linguagens: define assinaturas de métodos que o tipo precisa fornecer. Qualquer tipo — struct, enum ou até tipos primitivos — pode implementar uma trait.

Para implementar uma trait usamos impl NomeDaTrait for Tipo { ... }. A partir daí, os métodos da trait podem ser chamados como métodos normais do tipo. Traits permitem escrever código genérico que funciona com qualquer tipo que tenha o comportamento desejado, usando T: Trait ou impl Trait.

Traits também podem ter métodos padrão, isto é, implementações prontas que o tipo pode usar ou sobrescrever. Isso é útil para evitar repetição: o tipo só implementa o que é específico dele. Na biblioteca padrão existem várias traits importantes, como Display, Debug, Clone, PartialEq e Iterator.

Pontos-chave

  • Uma trait define comportamentos que tipos podem compartilhar, sem herança de classes.
  • Implementa-se com impl Trait for Tipo { ... } e o tipo passa a ter os métodos da trait.
  • Métodos padrão têm corpo na própria trait e podem ser sobrescritos pelo tipo.
  • Traits da std como Display, Clone e PartialEq podem ser derivadas ou implementadas manualmente.
  • Funções genéricas podem exigir uma trait com a sintaxe T: Trait ou impl Trait.
traits.rs
// Trait com um método obrigatório e um método padrão.
trait Animal {
    fn nome(&self) -> String; // obrigatório

    // método padrão: usa nome(), então quem implementa ganha de graça
    fn apresentar(&self) -> String {
        format!("Olá, eu sou {}", self.nome())
    }
}

struct Cachorro;
struct Gato;

impl Animal for Cachorro {
    fn nome(&self) -> String {
        String::from("Rex")
    }
}

impl Animal for Gato {
    fn nome(&self) -> String {
        String::from("Mimi")
    }

    // sobrescreve o método padrão
    fn apresentar(&self) -> String {
        format!("Miau! Eu sou {}", self.nome())
    }
}

// Função genérica que aceita qualquer tipo que implemente Animal.
fn saudar<T: Animal>(animal: &T) {
    println!("{}", animal.apresentar());
}

fn main() {
    let c = Cachorro;
    let g = Gato;
    saudar(&c);
    saudar(&g);
}

Exercícios

  1. 1
    Fácil

    Trait Forma com método padrão

    Crie uma trait Forma com o método obrigatório area(&self) -> f64 e um método padrão descricao(&self) -> String que retorne "Área: X", onde X é a área formatada com {:.2}. Implemente Forma para Retangulo { largura: f64, altura: f64 } e imprima a descrição de um retângulo 3.0 x 4.0.

    Resolver
  2. 2
    Médio

    Trait Resumo genérica

    Crie uma trait Resumo com o método obrigatório resumir(&self) -> String. Implemente-a para Noticia (campos titulo: String e autor: String), retornando "<titulo> por <autor>", e para Tweet (campo usuario: String e texto: String), retornando no máximo 20 caracteres do texto seguidos de "..." se o texto for maior que 20. Escreva uma função genérica mostrar<T: Resumo>(item: &T) que imprime o resumo.

    Resolver
  3. 3
    Desafio

    Trait Pilha com implementações diferentes

    Defina uma trait Pilha com os métodos obrigatórios push(&mut self, valor: i32), pop(&mut self) -> Option<i32> e o método padrão vazia(&self) -> bool que retorna true quando tamanho() for 0. Inclua também o método obrigatório tamanho(&self) -> usize. Implemente Pilha para uma struct PilhaVec que usa um Vec<i32> internamente e para uma struct PilhaLimitada que usa um Vec<i32> e recusa push quando já tem 3 elementos. No main, empilhe 1, 2, 3, 4 na PilhaLimitada, imprima o tamanho e desempilhe tudo imprimindo cada valor.

    Resolver

Continue estudando