O que são Generics em Swift e para que servem
Generics (programação genérica) é um dos recursos mais poderosos da linguagem Swift. Eles permitem escrever código flexível e reutilizável que funciona com qualquer tipo de dado, mantendo a tipagem forte. Em vez de criar funções ou estruturas separadas para Int, String, Double e outros tipos, você escreve um código universal que se adapta ao tipo específico durante a compilação.
Imagine que você precisa escrever uma função que troca dois valores. Sem Generics, você teria que criar sobrecargas para cada tipo:
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, isso é feito em uma única linha:
func swapValues<T>(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp}
var x = 10, y = 20swapValues(&x, &y) // funciona com Int
var str1 = "Hello", str2 = "World"swapValues(&str1, &str2) // funciona com StringPrincipais vantagens dos Generics
- Segurança de tipos: o compilador verifica os tipos em tempo de compilação, eliminando erros em tempo de execução
- Reutilização de código: código escrito uma vez funciona com qualquer tipo
- Desempenho: Generics em Swift são implementados em nível de compilador, sem perda de velocidade de execução
- Legibilidade: reduz a duplicação de código, tornando-o mais conciso
Funções Genéricas e Parâmetros de Tipo
Uma função genérica é declarada usando colchetes angulares <>, dentro dos quais é especificado o parâmetro de tipo (geralmente denotado pela letra T, mas qualquer nome pode ser usado). O parâmetro de tipo se torna um placeholder, que é substituído pelo tipo real quando a função é chamada.
Exemplo: encontrar um elemento em um array
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? { for (index, element) in array.enumerated() { if element == value { return index } } return nil}
let numbers = [1, 5, 3, 12, 7]if let index = findIndex(of: 12, in: numbers) { print("Índice do número 12: \\(index)") // Índice do número 12: 3}
let names = ["Ana", "Boris", "Victor"]if let index = findIndex(of: "Boris", in: names) { print("Índice do nome Boris: \\(index)") // Índice do nome Boris: 1}Observe a restrição T: Equatable. Isso é uma type constraint — exigimos que o tipo T suporte o protocolo Equatable, caso contrário a operação de comparação == seria impossível.
Tipos Genéricos: Structs, Classes e Enums
Generics funcionam não apenas com funções, mas também com tipos personalizados. Isso permite criar contêineres de dados universais.
Criando uma Stack Genérica
Um exemplo clássico é a estrutura de dados Stack (pilha), que funciona com qualquer tipo:
struct Stack<Element> { private var items: [Element] = [] mutating func push(_ item: Element) { items.append(item) } @discardableResult mutating func pop() -> Element? { return items.popLast() } func peek() -> Element? { return items.last } var isEmpty: Bool { return items.isEmpty }}
// Usovar intStack = Stack<Int>()intStack.push(10)intStack.push(20)print(intStack.pop() ?? "") // 20
var stringStack = Stack<String>()stringStack.push("Swift")stringStack.push("Generics")print(stringStack.pop() ?? "") // GenericsEnums Genéricos
Enums também podem ser ```