Rust Genéricos Intermediário
Genéricos
Genéricos em Rust permitem escrever código que funciona com múltiplos tipos sem duplicação, mantendo a segurança de tipos. Eles são resolvidos em tempo de compilação: o compilador gera uma versão especializada para cada tipo concreto usado, processo chamado monomorfização.
Funções genéricas usam parâmetros de tipo, como fn maior<T>(a: T, b: T) -> T. Para restringir os tipos aceitos, usamos trait bounds, por exemplo T: PartialOrd, que garantem que o tipo implemente os métodos necessários. A sintaxe where deixa as restrições mais legíveis em assinaturas complexas.
Genéricos são amplamente usados na biblioteca padrão: Vec<T>, Option<T>, Result<T, E> e funções como std::cmp::max. Eles evitam custo em tempo de execução, pois não há despacho dinâmico a menos que se use dyn Trait.
Pontos-chave
- Funções genéricas usam parâmetros de tipo entre
<>e podem ter múltiplos tipos. - Trait bounds (
T: Trait) restringem os tipos que podem ser usados com a função. - A cláusula
whereé equivalente a trait bounds, mas melhora a legibilidade. - Monomorfização gera código especializado para cada tipo concreto em tempo de compilação.
- Genéricos não têm custo de desempenho em relação a código escrito manualmente para cada tipo.
// Função genérica com trait bound: T deve implementar PartialOrd
fn maior<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b }
}
// Usando where para múltiplos bounds
fn imprime_maior<T>(a: T, b: T)
where
T: PartialOrd + std::fmt::Display,
{
let m = if a > b { a } else { b };
println!("O maior é: {}", m);
}
// Struct genérica
struct Par<T> {
primeiro: T,
segundo: T,
}
impl<T: PartialOrd + Copy> Par<T> {
fn maior(&self) -> T {
if self.primeiro > self.segundo {
self.primeiro
} else {
self.segundo
}
}
}
fn main() {
// Monomorfização: o compilador gera versões para i32 e f64
println!("{}", maior(10, 20));
println!("{}", maior(3.14, 2.71));
imprime_maior(100, 200);
let par = Par { primeiro: 5, segundo: 8 };
println!("Maior do par: {}", par.maior());
}