Generic ou Associated Type em Rust: repensando a escolha em 2025
\n\nSe você já programou em Rust e se perguntou "Por que em uma trait usa type Item e em outra usa <T>?", você não está sozinho. Essa pergunta no Stack Overflow acumulou milhares de visualizações e gerou dezenas de discussões. E embora existam respostas por lá, o tempo não para. Em 2025, o ecossistema Rust amadureceu, novos padrões surgiram, e entender a diferença entre generic type e associated type tornou-se não apenas teoria, mas uma necessidade para escrever código limpo e extensível.
Neste artigo, não vamos apenas recontar respostas antigas. Vamos analisar exemplos vivos, mostrar como fazer a escolha certa e dar dicas práticas que você pode aplicar hoje mesmo. Vamos lá!
\n\nMecânica básica: qual é a diferença?
\n\nAntes de nos aprofundarmos nas best practices, vamos relembrar os fundamentos. Tanto generic type quanto associated type são formas de tornar o código mais abstrato e reutilizável. Mas eles funcionam de maneiras diferentes.
\n\nGeneric type: flexibilidade através de parâmetros
\n\nQuando você escreve trait MyTrait<T>, você está dizendo: "Esta trait pode ser implementada para o mesmo tipo várias vezes, com diferentes T". Isso é flexibilidade máxima. Por exemplo:
trait Container<T> {\n fn get(&self) -> &T;\n}\n\nstruct NumberBox {\n value: u32,\n}\n\n// Um tipo, duas implementações\nimpl Container<u32> for NumberBox {\n fn get(&self) -> &u32 { &self.value }\n}\n\nimpl Container<String> for NumberBox {\n fn get(&self) -> &String { \n // Estranho, mas tecnicamente possível\n &"não é número".to_string() \n }\n}\n\nAssociated type: um tipo por implementação
\n\nQuando você usa trait MyTrait { type Output; }, você está dizendo: "Cada implementação desta trait pode definir seu próprio tipo Output, mas apenas uma vez". Isso proporciona maior clareza e é frequentemente usado na biblioteca padrão:
trait Iterator {\n type Item;\n fn next(&mut self) -> Option<Self::Item>;\n}\n\nstruct Counter {\n count: u32,\n}\n\n// Counter pode ter apenas um tipo Item\nimpl Iterator for Counter {\n type Item = u32;\n fn next(&mut self) -> Option<u32> {\n self.count += 1;\n Some(self.count)\n }\n}\n\nA diferença chave: generic type permite múltiplas implementações para um mesmo tipo, associated type permite apenas uma. À primeira vista, parece que generic é melhor, pois é mais flexível. Mas o diabo, como sempre, está nos detalhes.
\n\nQuando escolher Associated Type: o poder da inequivocidade
\n\n1. Relação lógica "um para um"
\n\nSe sua trait descreve uma entidade que possui um tipo "nativo", use associated type. O exemplo clássico é Iterator. Um iterador pode ter apenas um tipo de elementos que retorna. Se Item fosse generic, você poderia escrever:
// Ruim: Iterator<String> para o mesmo iterador?\nimpl Iterator<String> for Counter {\n // ...\n}\n\nIsso quebraria a lógica. Associated type garante que, para um determinado tipo de iterador, existe exatamente um tipo de elemento.
\n\nDica prática: Se sua trait tem um método que retorna um tipo "principal" e você não consegue imaginar por que alguém precisaria de uma segunda implementação com um tipo diferente para a mesma estrutura — use associated type sem medo.
\n\n2. Simplificação de assinaturas e melhoria da legibilidade
\n\nCompare duas traits:
\n\n// Com associated type\ntrait Graph {\n type Node;\n type Edge;\n fn neighbors(&self, node: &Self::Node) -> Vec<&Self::Edge>;\n}\n\n// Com generic type\ntrait Graph