Generics em Swift: guia completo com exemplos de código

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Generics em Swift: para que servem e como usar na prática



Generics (programação genérica) — é um dos recursos mais poderosos e fundamentais da linguagem Swift. Eles permitem escrever código flexível, type-safe e reutilizável, evitando duplicação. Se você já usou Array<Int>, Dictionary<String, Any> ou Optional<T>, já encontrou generics. Neste artigo, vamos entender para que servem, como aplicá-los corretamente e ver exemplos práticos para desenvolvimento iOS/macOS.



Para que servem os Generics em Swift?



O principal problema que os generics resolvem é a necessidade de escrever o mesmo código para diferentes tipos de dados. Sem eles, você teria que criar funções separadas para trabalhar com Int, String, Double e estruturas personalizadas. Isso leva à duplicação de código, erros e dificuldade de manutenção.



Principais vantagens dos generics:

  • Type-safety: o compilador verifica os tipos em tempo de compilação, prevenindo erros em tempo de execução.
  • Reutilização de código: uma função ou tipo funciona com qualquer tipo de dado.
  • Desempenho: generics em Swift são implementados através de especialização — o compilador cria código otimizado para cada tipo específico, comparável à escrita manual de código duplicado.
  • Legibilidade e abstração: o código se torna mais conciso e expressivo.


Vamos ver um exemplo simples. Suponha que precisamos de uma função para trocar os valores de duas variáveis. Sem generics, teríamos que escrever assim:



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}// ... e assim para cada tipo


Com generics, a solução se torna universal:



func swapValues<T>(_ a: inout T, _ b: inout T) {    let temp = a    a = b    b = temp}

var x = 10var y = 20swapValues(&x, &y) // Funciona com Int

var str1 = "Hello"var str2 = "World"swapValues(&str1, &str2) // Funciona com String


Como você pode ver, <T> é um placeholder (marcador) para o tipo. O compilador infere automaticamente o tipo específico na chamada da função.



Funções e tipos genéricos



Generics podem ser usados não apenas com funções, mas também com tipos personalizados: structs, classes e enums.



Funções genéricas

Além do placeholder simples, é possível impor restrições ao tipo. Por exemplo, uma função que funciona apenas com tipos que implementam o protocolo Equatable:



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, 5, 3, 12]if let index = findIndex(of: 5, in: numbers) { print("Índice encontrado: \\(index)") // Exibirá: Índice encontrado: 1}


A restrição <T: Equatable> garante que o operador == está definido para o tipo T.



Tipos genéricos (structs e classes)

Vamos criar uma pilha simples — uma estrutura de dados que funciona no princípio LIFO (Last In, First Out):



struct Stack<Element> {    private var items = [Element]()        mutating func push(_ item: Element) {        items.append(item)    }        mutating func pop() -> Element? {        return items.popLast()    }        var top: Element? {        return items.last    }}

var intStack = Stack<Int>()intStack.push(10)intStack.push(20)print(intStack.pop() ?? 0) // 20

var str

Blogs

Book Recommendations