Generics

Afinal o que são Generics?

Generics é um modo de reaproveitar um código substituindo um tipo 'T' por um tipo concreto. O compilador de Rust, gera implementações com o tipo concreto para cada uso do generic, ele faz isso para não perdemos performance em tempo de execução.

Declaração de um Generic

Para declarar um generic. Utilizamos o seguinte padrão struct Nome<T> { implementação }

#[derive(Debug)]
struct Cliente<T> {
    nome: String,
    ano_nascimento: u16,
    documento: T
}

fn main() {
    let cliente: Cliente<String> = Cliente::<String> {
        nome: String::from("Rust4Noobs"),
        ano_nascimento: 2021,
        documento: String::from("https://github.com/pgjbz/rust4noobs")
    };

    println!("{:#?}", cliente);
}

Do modo acima, o documento do Cliente, pode ser, uma String, um inteiro, um Enum, outra struct.

Implementando um método genérico.

Continuando o exemplo acima, lembra da parte sobre as structs onde implementamos o método estático new, como que ficaria para este exemplo usando generic?

#[derive(Debug)]
struct Cliente<T> {
    nome: String,
    ano_nascimento: u16,
    documento: T
}
//--declaração da struct
impl<T> Cliente<T> {
    fn new(nome: String, ano_nascimento: u16, documento: T) -> Self {
        Self {
            nome,
            ano_nascimento,
            documento
        }
    }
}

fn main() {
    // T = String
    let cliente = Cliente::new(
        String::from("Rust4Noobs"),
        2021,
        String::from("350.123.123-23")
    );
    println!("{:#?}", cliente);

    // T = u32
    let cliente2 = Cliente::new(
        String::from("Rust4Noobs"),
        2021,
        35012312323
    );
    println!("{:#?}", cliente2);
}

Eu não necessariamente tenho um limite de quantidade de Generics em uma struct. Por exemplo, e se nessa minha struct de Cliente eu quiser que o ano_nascimento possa ter outro tipo além de u16?


#![allow(unused)]
fn main() {
#[derive(Debug)]
struct Cliente<T, U> {
    nome: String,
    ano_nascimento: U,
    documento: T
}

impl<T, U> Cliente<T, U> {
     fn new(nome: String, ano_nascimento: U, documento: T) -> Self {
        Self {
            nome,
            ano_nascimento,
            documento
        }
    }
}
}

Funções/Métodos com Generics

Claro generics não apenas para structs, métodos/funções também podem ter. Vamos para o seguinte exemplo.

fn maior<T>(lista: &[T]) -> T {
    let mut maior = lista[0];
    for &item in lista {
        if item > maior {
            maior = item;
        }
    }
    maior
}

fn main() {
    let arr: [u8; 4] = [2,4,1,11];
    let maior = maior(&arr);
    println!("Maior elemento: {}", maior);
}

O código acima, esta quaaase funcionando, mas para funcionar precisamos explicar outro conceito, as 'traits'