Por que sem genéricos seu código Swift rapidamente se transformará em espaguete
\n\nImagine que você está escrevendo um aplicativo de entrega de pizza. Você precisa processar pedidos, clientes, entregadores — e toda vez escrever o mesmo código para ordenação, busca ou filtragem, mudando apenas o tipo de dados? Parece o pesadelo de qualquer desenvolvedor. É aqui que entram em cena os Generics em Swift — uma ferramenta poderosa que permite escrever código flexível e reutilizável sem sacrificar a segurança de tipos.
\n\nNeste artigo, não vamos apenas passar pela sintaxe. Vamos analisar como a tipagem em Swift e os genéricos trabalham juntos para que seu código não seja apenas bonito, mas também confiável. Você aprenderá como se livrar do copia e cola, como usar protocol com associatedtype e por que as cláusulas where são suas melhores amigas.
O que são Generics e por que eles mudam as regras do jogo
\n\nGenerics (genéricos, tipos genéricos) são um mecanismo que permite escrever funções, estruturas, classes e enumerações que funcionam com qualquer tipo de dado que satisfaça certas condições. Em vez de escrever três versões de uma função para Int, String e Double, você escreve uma que funciona com qualquer tipo.
Em Swift, a tipagem é forte e estática. Isso significa que o compilador verifica os tipos em tempo de compilação. Os Generics não quebram essa regra — eles a tornam mais flexível. O compilador infere o tipo específico ao chamar a função, mas as verificações permanecem.
\n\nPrimeiro exemplo: função sem genéricos
\n\n// Ruim: três funções para três tipos\nfunc swapInts(_ a: inout Int, _ b: inout Int) {\n let temp = a\n a = b\n b = temp\n}\n\nfunc swapStrings(_ a: inout String, _ b: inout String) {\n let temp = a\n a = b\n b = temp\n}\n\nfunc swapDoubles(_ a: inout Double, _ b: inout Double) {\n let temp = a\n a = b\n b = temp\n}\n\nAgora — a solução elegante com genéricos:
\n\n// Bom: uma função para todos os tipos\nfunc swapValues<T>(_ a: inout T, _ b: inout T) {\n let temp = a\n a = b\n b = temp\n}\n\n// Uso:\nvar x = 10\nvar y = 20\nswapValues(&x, &y) // T = Int\n\nvar name1 = "Alice"\nvar name2 = "Bob"\nswapValues(&name1, &name2) // T = String\n\nViu a mágica? <T> é um placeholder (marcador) de tipo. O compilador substitui pelo tipo real na chamada. Essa é a base da programação genérica em Swift.
Tipos genéricos: criando sua própria pilha universal
\n\nOs genéricos em Swift funcionam não apenas com funções, mas também com tipos inteiros. Vamos criar uma estrutura Stack — o exemplo clássico de estrutura de dados LIFO (Last In, First Out).
struct Stack<Element> {\n private var items: [Element] = []\n \n mutating func push(_ item: Element) {\n items.append(item)\n }\n \n mutating func pop() -> Element? {\n return items.popLast()\n }\n \n func peek() -> Element? {\n return items.last\n }\n \n var count: Int {\n return items.count\n }\n}\n\n// Uso:\nvar intStack = Stack<Int>()\nintStack.push(1)\nintStack.push(2)\nintStack.push(3)\nprint(intStack.pop() ?? 0) // 3\n\nvar stringStack = Stack<String>()\nstringStack.push("Swift")\nstringStack.push("Generics")\nprint(stringStack.peek() ?? "") // "Generics"\n\nObserve: o tipo Element é especificado entre colchetes angulares na declaração da estrutura. Ao criar uma instância, o concretizamos — Stack<Int> ou Stack<String>. O compilador garante que você não possa acidentalmente colocar uma string em uma pilha de inteiros.
Protocol com associatedtype: genéricos para protocolos
\n\nEm Swift, os protocolos não podem ter parâmetros genéricos diretamente. Em vez disso, us