Swift: Valores de Retorno e Tipos de Função do Swift
As funções podem retornar mais do que tipos básicos — podem retornar múltiplos valores e até retornar outras funções. Esta lição explora todo o espectro de valores de retorno de funções Swift e o conceito central de funções como cidadãs de primeira classe.
1. O Que Você Vai Aprender
- Retornar múltiplos valores usando tuplas
- Usar funções como parâmetros e valores de retorno
- Nomear tipos de função com
typealias - Trabalhar com funções aninhadas e suas regras de escopo
- Entender os fundamentos da sobrecarga de funções
2. Uma História Real de um Desenvolvedor de E-Commerce
(1) Problema: Escrever uma nova função para cada nível de desconto
Charlie está construindo um motor de promoções. Diferentes níveis de usuário recebem descontos diferentes:
func vipDiscount(_ price: Double) -> Double {
return price * 0.8
}
func memberDiscount(_ price: Double) -> Double {
return price * 0.9
}
func normalDiscount(_ price: Double) -> Double {
return price
}
Toda vez que um novo tipo de desconto é adicionado, ele escreve outra função. Quando as regras de promoção mudam, uma pilha de código precisa ser atualizada. A troca de estratégia depende de if-else escrito à mão; não há como configurá-la dinamicamente.
(2) A Solução com Funções como Tipos
No Swift, funções são "cidadãs de primeira classe" — você pode passá-las e armazená-las como Int ou String:
// Define a discount strategy type
typealias DiscountStrategy = (Double) -> Double
// Use a function dictionary instead of if-else
let strategies: [String: DiscountStrategy] = [
"vip": { $0 * 0.8 },
"member": { $0 * 0.9 },
"normal": { $0 }
]
// Switch strategy in one line
func applyDiscount(_ price: Double, level: String) -> Double {
return strategies[level]?(price) ?? price
}
(3) Resultado: Estratégias configuráveis, código extensível
| Dimensão | Antes | Depois |
|---|---|---|
| Adicionar um desconto | Escrever função + modificar if-else | Adicionar uma entrada no dicionário |
| Troca de estratégia | if-else fixo no código | Seleção dinâmica por chave |
| Reutilização | Cada estratégia isolada, não combinável | Funções combináveis e passáveis |
| Cobertura de testes | Um teste por estratégia | Framework de teste unificado |
3. Múltiplos Valores de Retorno e Sobrecarga de Funções
(1) Retornando Múltiplos Valores com Tuplas
As tuplas do Swift permitem que uma função retorne vários valores de uma vez:
graph LR
A[Função retorna tupla] --> B[Elemento 1]
A --> C[Elemento 2]
A --> D[Elemento 3]
B --> E["Acesso nomeado: resultado.nome"]
C --> F["Acesso nomeado: resultado.idade"]
D --> G["Acesso indexado: resultado.0"]
| Abordagem | Sintaxe | Quando Usar |
|---|---|---|
| Tupla anônima | -> (Int, String) |
Agrupamento simples, nomes não importantes |
| Tupla nomeada | -> (soma: Int, media: Double) |
Acesso semântico aos campos necessário |
| Tupla optional | -> (Int, String)? |
Função pode falhar e retornar nil |
▶ Exemplo: Análise Estatística de Array
// ============================================
// Return multiple statistics using a named tuple
// ============================================
func analyze(_ numbers: [Int]) -> (count: Int, sum: Int, average: Double) {
let count = numbers.count
let sum = numbers.reduce(0, +)
let average = count > 0 ? Double(sum) / Double(count) : 0
return (count, sum, average)
}
let result = analyze([85, 90, 78, 92, 88])
print("Count: \(result.count)")
print("Sum: \(result.sum)")
print("Average: \(result.average)")
Saída:
TEXT 📖 Somente leituraCount: 5 Sum: 433 Average: 86.6
(2) Sobrecarga de Funções
O Swift suporta múltiplas funções com o mesmo nome, desde que os tipos ou quantidades de parâmetros sejam diferentes:
func area(width: Double, height: Double) -> Double {
return width * height
}
func area(radius: Double) -> Double {
return Double.pi * radius * radius
}
print("Rectangle area: \(area(width: 10, height: 5))")
print("Circle area: \(area(radius: 3))")
Saída:
TEXT 📖 Somente leituraRectangle area: 50.0 Circle area: 28.274333882308138
4. Funções como Tipos
(1) Sintaxe de Tipo de Função
Cada função tem um tipo específico, determinado pelos tipos de seus parâmetros e tipo de retorno:
| Assinatura da Função | Tipo da Função |
|---|---|
func somar(a: Int, b: Int) -> Int |
(Int, Int) -> Int |
func log() -> Void |
() -> Void ou () -> () |
func saudar(_ nome: String) |
(String) -> Void |
func dobrar(_ n: Int) -> Int |
(Int) -> Int |
▶ Exemplo: Funções como Variáveis e Parâmetros
// ============================================
// Functions passed as variables and parameters
// ============================================
// 1. Define two simple functions
func add(_ a: Int, _ b: Int) -> Int { return a + b }
func multiply(_ a: Int, _ b: Int) -> Int { return a * b }
// 2. Function type variable
var operation: (Int, Int) -> Int = add
print("add: \(operation(3, 4))")
// 3. Swap the function
operation = multiply
print("multiply: \(operation(3, 4))")
// 4. Function as a parameter
func execute(_ a: Int, _ b: Int, using op: (Int, Int) -> Int) -> Int {
return op(a, b)
}
print("execute add: \(execute(5, 6, using: add))")
print("execute multiply: \(execute(5, 6, using: multiply))")
Saída:
TEXT 📖 Somente leituraadd: 7 multiply: 12 execute add: 11 execute multiply: 30
(2) typealias para Nomeação de Tipo de Função
Quando um tipo de função é complexo ou usado em muitos lugares, dê um nome a ele com typealias:
// Define a function type alias
typealias IntOperation = (Int, Int) -> Int
// Use the alias for variables and parameters
func calculate(_ a: Int, _ b: Int, using op: IntOperation) -> Int {
return op(a, b)
}
let op: IntOperation = { $0 * $0 + $1 * $1 }
print("Result: \(calculate(3, 4, using: op))")
Saída:
TEXT 📖 Somente leituraResult: 25
5. Funções Aninhadas e Funções que Retornam Funções
(1) Funções Aninhadas
Você pode definir funções dentro de outras funções. As funções internas são acessíveis apenas dentro do escopo da função externa:
// ============================================
// Nested functions: define helper functions inside
// ============================================
func formatFullName(first: String, last: String) -> String {
// Nested helper
func capitalize(_ text: String) -> String {
return text.prefix(1).uppercased() + text.dropFirst().lowercased()
}
return "\(capitalize(first)) \(capitalize(last.uppercased()))"
}
print(formatFullName(first: "alice", last: "JOHNSON"))
Saída:
TEXT 📖 Somente leituraAlice Johnson
(2) Funções que Retornam Funções
Outra forma de funções de ordem superior — retornar uma função:
// ============================================
// Function factory: generate different strategy functions by parameter
// ============================================
func makeMultiplier(factor: Double) -> (Double) -> Double {
func multiplier(_ value: Double) -> Double {
return value * factor
}
return multiplier
}
let double = makeMultiplier(factor: 2)
let triple = makeMultiplier(factor: 3)
print("double(5) = \(double(5))")
print("triple(5) = \(triple(5))")
Saída:
TEXT 📖 Somente leituradouble(5) = 10.0 triple(5) = 15.0
▶ Exemplo: Fábrica de Estratégia de Desconto
// ============================================
// Discount factory with nested functions and function return values
// ============================================
typealias DiscountRule = (Double) -> Double
func discountFor(level: String) -> DiscountRule? {
// Nested discount functions
func vip(_ price: Double) -> Double { return price * 0.8 }
func member(_ price: Double) -> Double { return price * 0.9 }
func normal(_ price: Double) -> Double { return price }
switch level {
case "vip": return vip
case "member": return member
case "normal": return normal
default: return nil
}
}
if let rule = discountFor(level: "vip") {
print("VIP discount price: $\(rule(100))")
}
Saída:
TEXT 📖 Somente leituraVIP discount price: $80.0
6. Exemplo Completo: Motor de Promoções
// ============================================
// Full example: Promotions engine
// Combines: multiple returns + function types + typealias + nested functions
// ============================================
import Foundation
// 1. Define function type aliases
typealias Discount = (Double) -> Double
typealias DiscountResult = (original: Double, final: Double, saved: Double)
// 2. Discount factory — returns a nested function
func createDiscount(minSpend: Double, rate: Double) -> Discount {
func discount(_ price: Double) -> Double {
return price >= minSpend ? price * (1 - rate) : price
}
return discount
}
// 3. Multiple return values — return discount result details
func applyDiscount(price: Double, rule: Discount) -> DiscountResult {
let final = rule(price)
let saved = price - final
return (price, final, saved)
}
// 4. Function as parameter — batch process orders
func processOrders(_ prices: [Double], using rule: Discount) -> [DiscountResult] {
return prices.map { applyDiscount(price: $0, rule: rule) }
}
// 5. Use the promotions engine
let memberRule = createDiscount(minSpend: 50, rate: 0.15)
let orderResults = processOrders([120, 45, 200], using: memberRule)
for result in orderResults {
print("$\(result.original) → $\(result.final) (saved $\(result.saved))")
}
Saída:
TEXT 📖 Somente leitura$120.0 → $102.0 (saved $18.0) $45.0 → $45.0 (saved $0.0) $200.0 → $170.0 (saved $30.0)
❓ Perguntas Frequentes
P: Por que os tipos de função ignoram os rótulos de argumento? R: Os tipos de função se concentram na correspondência de tipo — os tipos dos parâmetros e o valor de retorno. Os rótulos fazem parte da convenção de chamada externa e não afetam a compatibilidade de tipo. Assim,
(a: Int) -> Voide(b: Int) -> Voidsão o mesmo tipo. P: Qual é a diferença entre funções aninhadas e closures? R: Funções aninhadas são funções internas nomeadas. Closures são tipicamente blocos de código anônimos. Funções aninhadas podem capturar variáveis externas e se comportam de forma semelhante às closures, mas têm um nome e podem ser chamadas repetidamente. P: O typealias pode ser usado com tipos de função genéricos complexos? R: Sim. Por exemplo,typealias Transformador<T> = (T) -> Tdefine um alias de tipo de função genérico. No entanto, no Swift, typealias não pode expressar diretamente restrições genéricas — outros mecanismos são necessários para isso. P: É melhor retornar uma tupla ou uma struct personalizada? R: Use tuplas para combinações simples (ex.: retornar dois Ints). Use structs para cenários semanticamente complexos (campos nomeados com documentação). Tuplas são adequadas para agrupamentos ad-hoc; structs são adequadas para APIs públicas. P: A sobrecarga de funções prejudica o desempenho em tempo de execução? R: Não. A resolução de sobrecarga acontece em tempo de compilação (despacho estático), sem sobrecarga em tempo de execução. O compilador Swift seleciona a versão correta com base nos tipos e na quantidade de parâmetros durante a compilação.
📖 Resumo
- Use tuplas nomeadas para retornar múltiplos valores semanticamente significativos de uma única função
- Funções são cidadãs de primeira classe: atribua a variáveis, passe como parâmetros, retorne como valores
typealiasnomeia tipos de função, melhorando a legibilidade e reutilização do código- Funções aninhadas têm escopo dentro da função externa e são ideais para encapsular lógica auxiliar
- Funções que retornam funções (fábricas de funções) geram dinamicamente estratégias com comportamentos diferentes
- A sobrecarga de funções fornece múltiplas versões de uma função com tipos ou quantidades diferentes de parâmetros
📝 Exercícios
- Iniciante: Escreva uma função
dividir(_:_:)que retorna tanto o quociente quanto o resto via tupla. Chame e imprima o resultado dedividir(17, 5). - Intermediário: Escreva uma função
transformarpara arrays[Int]que recebe um parâmetro do tipo(Int) -> Inte retorna um novo array. Depois defina um typealias para esse tipo de função. - Desafio: Implemente uma "cadeia de operações" — escreva uma função
encadear(_:_:)que recebe um valorIntinicial e um array de funções[(Int) -> Int], aplica cada função ao valor em sequência e retorna o resultado final. Crie três funções de transformação e componha uma cadeia.