Introdução: o que são Generics e por que são importantes em Swift
Generics (tipos genéricos) — uma das capacidades mais poderosas e elegantes da linguagem Swift. Eles permitem escrever código flexível e reutilizável que funciona com qualquer tipo de dado, mantendo ao mesmo tempo a tipagem estrita. Se você já usou Array<Int>, Dictionary<String, Any> ou Optional<T> — você já encontrou Generics.
Neste artigo, vamos analisar para que servem os Generics em Swift, como funcionam na prática e mostrar exemplos para desenvolvimento iOS/macOS. O material será útil tanto para iniciantes quanto para desenvolvedores experientes que desejam estruturar seus conhecimentos.
1. Para que servem os Generics em Swift?
O objetivo principal dos Generics é evitar duplicação de código e aumentar sua segurança. Imagine que você precisa escrever uma função que troque dois valores. Sem Generics, você teria que criar funções separadas para Int, String, Double e assim por diante:
func swapInts(_ a: inout Int, _ b: inout Int) { let temp = a a = b b = temp}
func swapStrings(_ a: inout String, _ b: inout String) { let temp = a a = b b = temp}Com Generics, esta tarefa é resolvida com uma única função universal:
func swapValues<T>(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp}
var x = 10, y = 20swapValues(&x, &y) // x = 20, y = 10
var name1 = "Alice", name2 = "Bob"swapValues(&name1, &name2) // name1 = "Bob", name2 = "Alice"Vantagens dos Generics:
- Reutilização de código: uma função/tipo funciona com qualquer tipo de dado.
- Type safety: o compilador verifica os tipos em tempo de compilação, eliminando erros de execução.
- Desempenho: Generics em Swift são implementados através de polimorfismo em tempo de compilação (especialização), portanto não há custos adicionais de casting de tipos.
- Legibilidade: o código se torna mais abstrato e conciso.
2. Funções Genéricas: sintaxe e exemplos
Uma função genérica é declarada usando colchetes angulares <T>, onde T é um placeholder para o tipo. Você pode usar qualquer identificador: T, Element, Value, etc.
Exemplo básico: encontrar o índice de um elemento
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? { for (index, item) in array.enumerated() { if item == value { return index } } return nil}
let numbers = [1, 3, 5, 7, 9]if let index = findIndex(of: 5, in: numbers) { print("Índice do número 5: \\(index)") // Índice do número 5: 2}Observe a restrição <T: Equatable>. Isto é um type constraint. Ele diz ao compilador: "T deve estar em conformidade com o protocolo Equatable". Sem esta restrição, o operador == não estaria disponível.
Múltiplos parâmetros de tipo
Você pode usar vários parâmetros de tipo:
func combine<T, U>(_ first: T, with second: U) -> String { return "\\(first) + \\(second)"}
let result = combine(42, with: "resposta")print(result) // 42 + resposta3. Tipos Genéricos: criação de structs e classes genéricas
Generics não se aplicam apenas a funções, mas também a tipos. O exemplo mais popular é Array<Element>. Vamos criar nossa própria struct genérica — uma pilha (LIFO):
struct Stack<Element> { private var items: [Element] = [] mutating func push(_ item: Element) { items.append(item
```