SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
12.11.2024

Aula 008: Traits e Generics em Rust

Nesta aula, vamos explorar duas características poderosas do Rust: Traits e Generics. Essas características nos permitem escrever código flexível e reutilizável. Vamos lá!

O que são Traits?

Traits em Rust são semelhantes a interfaces em outras linguagens de programação. Elas definem um conjunto de métodos que podem ser implementados por diferentes tipos. Isso permite que você especifique comportamentos compartilhados entre diferentes tipos sem conhecer suas implementações concretas.

Definindo um Trait

Vamos definir um trait simples chamado Falar, que possui um método: falar.

trait Falar {
    fn falar(&self) -> String;
}

Implementando um Trait

Agora, vamos implementar o trait Falar para diferentes tipos.

struct Cachorro;
struct Gato;

impl Falar for Cachorro {
    fn falar(&self) -> String {
        String::from("Au Au!")
    }
}

impl Falar for Gato {
    fn falar(&self) -> String {
        String::from("Miau!")
    }
}

Usando Traits

Agora que temos nosso trait definido e implementado, podemos escrever uma função que aceita qualquer tipo que implemente o trait Falar.

fn deixar_o_animal_falar<T: Falar>(animal: T) {
    println!("{}", animal.falar());
}

fn main() {
    let cachorro = Cachorro;
    let gato = Gato;

    deixar_o_animal_falar(cachorro);
    deixar_o_animal_falar(gato);
}

Quando você executar o código acima, verá a seguinte saída:

Au Au!
Miau!

O que são Generics?

Generics permitem que você escreva funções, structs e enums que podem operar em diferentes tipos de dados sem sacrificar a segurança de tipos. Em vez de especificar tipos concretos, você pode usar parâmetros de tipos.

Usando Generics em Funções

Vamos criar uma função simples que pode aceitar qualquer tipo e retornar seu comprimento.

fn obter_comprimento<T>(item: T) -> usize 
where 
    T: std::ops::Deref<Target = str> {
    item.len()
}

Neste caso, T pode ser qualquer tipo que desreferencia para um slice de string.

Usando Generics em Structs

Você também pode definir structs que usam generics. Vamos definir uma struct de par simples.

struct Par<T, U> {
    x: T,
    y: U,
}

impl<T, U> Par<T, U> {
    fn novo(x: T, y: U) -> Par<T, U> {
        Par { x, y }
    }

    fn x(&self) -> &T {
        &self.x
    }

    fn y(&self) -> &U {
        &self.y
    }
}

Usando Structs com Generics

Agora, vamos usar nossa struct Par com diferentes tipos.

fn main() {
    let inteiro_e_flotante = Par::novo(1, 2.5);
    let string_e_char = Par::novo(String::from("Olá"), 'A');

    println!("Inteiro: {}, Flotante: {}", inteiro_e_flotante.x(), inteiro_e_flotante.y());
    println!("String: {}, Char: {}", string_e_char.x(), string_e_char.y());
}

Quando você executar o código acima, verá:

Inteiro: 1, Flotante: 2.5
String: Olá, Char: A

Conclusão

Nesta aula, aprendemos como definir e implementar traits e como usar generics em Rust. Utilizando essas características poderosas, podemos escrever código que seja ao mesmo tempo flexível e reutilizável. Traits nos permitem definir comportamentos compartilhados entre diferentes tipos, enquanto generics nos capacitam a escrever funções e structs que podem trabalhar com qualquer tipo.

À medida que você continua sua jornada em Rust, aproveitar o poder das traits e generics ajudará você a construir um código mais robusto e abstrato. Boas codificações!

Did you like this article? Rate it from 1 to 5:

Thank you for voting!