<h2>Introdução aos Generics em Rust</h2>
<p>Generics são um dos pilares da programação moderna e Rust oferece uma implementação particularmente robusta e segura. Em essência, generics permitem que você escreva código parametrizado por tipo, eliminando duplicação sem comprometer a segurança de tipos. Diferentemente de linguagens como Java ou C#, Rust realiza a monomorphização em tempo de compilação: para cada tipo concreto usado com um generic, o compilador gera uma cópia especializada do código. Isso significa zero custo em runtime e máxima performance.</p>
<p>Neste artigo, exploraremos como trabalhar com generics em funções e structs, começando pelos conceitos fundamentais até padrões avançados. Você aprenderá a aproveitar o poder dos generics para escrever código reutilizável, type-safe e eficiente.</p>
<h2>Generics em Funções</h2>
<h3>Sintaxe Básica</h3>
<p>Funções genéricas recebem um parâmetro de tipo entre colchetes angulares <code><T></code>. O identificador <code>T</code> é uma convenção (como em Java), mas você pode usar qualquer nome válido. Veja este exemplo simples:</p>
<pre><code class="language-rust">fn maior<T: std::cmp::PartialOrd>(a: T, b: T) -> T {
if a > b {
a
} else {
b
}
}
fn main() {
println!("{}", maior(10, 20)); // 20
println!("{}", maior(3.14, 2.71)); // 3.14
println!("{}", maior("apple", "zebra")); // "zebra"
}</code></pre>
<p>Aqui, <code>T: std::cmp::PartialOrd</code> é um <strong>trait bound</strong> — uma restrição indicando que <code>T</code> deve implementar a trait <code>PartialOrd</code> (comparação). Sem isso, o compilador não saberia como comparar dois valores de tipo <code>T</code>.</p>
<h3>Múltiplos Parâmetros de Tipo</h3>
<p>Você pode parametrizar uma função com vários tipos simultaneamente. Isso é especialmente útil para operações que envolvem tipos diferentes:</p>
<pre><code class="language-rust">fn tupla_invertida<T, U>(a: T, b: U) -> (U, T) {
(b, a)
}
fn main() {
let resultado = tupla_invertida(42, "Rust");
println!("{:?}", resultado); // ("Rust", 42)
}</code></pre>
<p>Cada parâmetro de tipo é independente e pode ter suas próprias restrições. Você pode combinar múltiplos trait bounds usando <code>+</code>:</p>
<pre><code class="language-rust">fn processar<T: std::fmt::Display + std::clone::Clone>(valor: T) {
let copia = valor.clone();
println!("Original: {}, Cópia: {}", valor, copia);
}
fn main() {
processar(String::from("Olá, Rust!"));
}</code></pre>
<h2>Generics em Structs</h2>
<h3>Definindo Structs Genéricas</h3>
<p>Structs genéricas armazenam valores de tipo parametrizado. Isso é fundamental para criar contêineres reutilizáveis:</p>
<pre><code class="language-rust">struct Caixa<T> {
conteudo: T,
}
impl<T> Caixa<T> {
fn novo(conteudo: T) -> Caixa<T> {
Caixa { conteudo }
}
fn obter_conteudo(&self) -> &T {
&self.conteudo
}
fn consumir(self) -> T {
self.conteudo
}
}
fn main() {
let caixa_int = Caixa::novo(42);
let caixa_str = Caixa::novo("Rust é incrível");
println!("{}", caixa_int.obter_conteudo());
println!("{}", caixa_str.obter_conteudo());
let valor = caixa_int.consumir();
println!("Consumido: {}", valor);
}</code></pre>
<p>Observe que <code>impl<T></code> indica que os métodos funcionam para <em>qualquer</em> tipo <code>T</code>. A sintaxe <code>Caixa<T>::novo()</code> retorna uma instância parametrizada.</p>
<h3>Structs com Múltiplos Generics</h3>
<p>Assim como funções, structs podem ter vários parâmetros de tipo:</p>
<pre><code class="language-rust">struct Par<T, U> {
primeiro: T,
segundo: U,
}
impl<T: std::fmt::Debug, U: std::fmt::Debug> Par<T, U> {
fn exibir(&self) {
println!("Primeiro: {:?}, Segundo: {:?}", self.primeiro, self.segundo);
}
}
fn main() {
let par = Par {
primeiro: 10,
segundo: "texto",
};
par.exibir();
}</code></pre>
<p>Aqui, o trait bound <code>Debug</code> é aplicado apenas aos métodos que precisam exibir os valores. Outras operações na struct podem não exigir essa restrição.</p>
<h2>Trait Bounds e Implementações Especializadas</h2>
<h3>Restrições Avançadas</h3>
<p>Rust permite definir implementações especializadas para casos específicos. Isso é chamado de <strong>specialization</strong> (ainda em fase experimental) ou uso estratégico de trait bounds:</p>
<pre><code class="language-rust">struct Conteiner<T> {
items: Vec<T>,
}
// Implementação geral
impl<T> Conteiner<T> {
fn novo() -> Self {
Conteiner { items: Vec::new() }
}
fn adicionar(&mut self, item: T) {
self.items.push(item);
}
}
// Implementação especializada para tipos Clone
impl<T: Clone> Conteiner<T> {
fn duplicar_primeiro(&mut self) {
if let Some(primeiro) = self.items.first() {
self.items.push(primeiro.clone());
}
}
}
fn main() {
let mut cont = Conteiner::novo();
cont.adicionar(5);
cont.adicionar(10);
cont.duplicar_primeiro();
println!("{:?}", cont.items); // [5, 10, 5]
}</code></pre>
<p>A implementação de <code>duplicar_primeiro()</code> está disponível apenas se <code>T</code> implementa <code>Clone</code>. Isso oferece flexibilidade sem comprometer a segurança.</p>
<h3>Where Clauses</h3>
<p>Para trait bounds complexos, a syntax <code>where</code> melhora a legibilidade:</p>
<pre><code class="language-rust">fn processar_pares<T, U>(a: T, b: U)
where
T: std::fmt::Display + std::cmp::PartialOrd<U>,
U: std::fmt::Display,
{
println!("Processando: {} e {}", a, b);
}</code></pre>
<p>A cláusula <code>where</code> deixa claro quais restrições se aplicam a cada tipo, especialmente útil em assinaturas complexas.</p>
<h2>Conclusão</h2>
<p>Generics em Rust permitem escrever código altamente reutilizável mantendo segurança de tipos total. Os três pontos-chave são: <strong>(1) funções genéricas reduzem duplicação</strong> parametrizando operações comuns; <strong>(2) structs genéricas criam contêineres flexíveis</strong> que funcionam com qualquer tipo, da mesma forma que <code>Vec<T></code> e <code>Option<T></code>; <strong>(3) trait bounds garantem que operações específicas estejam disponíveis</strong>, evitando erros em tempo de compilação. Dominando generics, você terá as ferramentas para escrever bibliotecas robustas e extensíveis.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://doc.rust-lang.org/book/ch10-00-generics.html" target="_blank" rel="noopener noreferrer">Rust Book - Generic Types, Traits, and Lifetimes</a></li>
<li><a href="https://doc.rust-lang.org/rust-by-example/generics.html" target="_blank" rel="noopener noreferrer">Rust by Example - Generics</a></li>
<li><a href="https://doc.rust-lang.org/reference/items/generics.html" target="_blank" rel="noopener noreferrer">Rust Reference - Generics</a></li>
<li><a href="https://doc.rust-lang.org/book/ch17-02-using-trait-objects.html" target="_blank" rel="noopener noreferrer">Trait Objects vs Generics</a></li>
</ul>