Uma observação comum em revisões de código é: “Vamos trocar isto por guard”. O código funciona exatamente igual com if, então por que mudar? Parece uma questão de estilo, mas guard é uma construção criada pelo Swift para incentivar um estilo específico: saída antecipada e caminho feliz alinhado à esquerda.
Este é o episódio 4 da série Swift básico. No artigo sobre opcionais, apresentei rapidamente guard let como ferramenta de desembrulho. Agora vamos examinar o próprio guard: como ele difere de if, o que o compilador garante e quando if — e não guard — é a escolha certa.
Se quiser revisar o estado de ausência de valor, leia O que os opcionais do Swift realmente são e como desembrulhá-los primeiro.
O problema — a pirâmide do caos
Primeiro, veja um código de antes da existência de guard. Uma função de cadastro tem muita coisa para validar: se a entrada é nil, se o formato está correto e se os termos foram aceitos.
func signUp(email: String?, password: String?, agreed: Bool) {
if let email = email {
if isValidEmail(email) {
if let password = password {
if password.count >= 8 {
if agreed {
// O que realmente queríamos fazer
createAccount(email, password)
} else {
showError("É necessário aceitar os termos")
}
} else {
showError("A senha é curta demais")
}
} else {
showError("Digite uma senha")
}
} else {
showError("O formato do e-mail é inválido")
}
} else {
showError("Digite um e-mail")
}
}
A indentação chega a cinco níveis. Esse formato é chamado de pirâmide do caos e não é apenas feio: ele também aumenta o custo de leitura. O trabalho principal da função, createAccount, fica enterrado no nível mais profundo, enquanto o tratamento de falha, else, fica visualmente distante de cada condição. Para responder “O que acontece se a senha for curta demais?”, é preciso conferir chaves visualmente enquanto se rola a tela.
A resposta de guard — falhas primeiro, trabalho principal no caminho plano
Reescrevendo a mesma função com guard, temos isto.
func signUp(email: String?, password: String?, agreed: Bool) {
guard let email, isValidEmail(email) else {
return showError("Verifique o e-mail")
}
guard let password, password.count >= 8 else {
return showError("Verifique a senha (8 caracteres ou mais)")
}
guard agreed else {
return showError("É necessário aceitar os termos")
}
createAccount(email, password)
}
A estrutura foi invertida. Cada condição fica ligada ao seu tratamento de falha em um único bloco, enquanto o trabalho principal, depois de passar por todos os controles, permanece plano no fim da função. O olhar do leitor só precisa descer pelo código uma vez. A estrutura lógica da função, “pré-condições e depois trabalho principal”, agora coincide com a estrutura visual.
Esse benefício costuma ser chamado de alinhamento à esquerda do caminho feliz. O fluxo normal fica no nível zero de indentação, enquanto apenas as situações excepcionais entram em blocos. Mesmo quando a função cresce, permanece a consistência: seguir a extremidade esquerda revela o cenário normal.
A diferença real em relação a if — duas garantias impostas pelo compilador
É hora de perguntar: “if também não pode fazer uma saída antecipada?” Pode: escreva como if email == nil { return }. Mas guard é mais que um if invertido: ele oferece duas garantias impostas pelo compilador.
Primeiro, o bloco else precisa sair do escopo. Dentro do else de guard, o escopo atual deve ser abandonado com return, throw, continue, break ou fatalError; caso contrário, a compilação falha. Com if, é possível verificar a condição e esquecer o return, mas guard captura esse erro em tempo de compilação. “Se chegou até aqui, a condição é verdadeira” deixa de ser convenção e vira garantia.
Segundo, o valor desembrulhado permanece disponível em todo o código abaixo de guard. O binding de if let só vale dentro do bloco if, enquanto o binding de guard let pode ser usado em todo o escopo restante. O valor que passou pelo controle permanece disponível até o fim da função, sem separar artificialmente desembrulho e uso.
Juntas, essas garantias fazem guard também funcionar como documentação. O conjunto de guards no início de uma função declara sua lista de pré-condições. Contratos que não cabem na assinatura podem ser lidos nas primeiras linhas do corpo.
Quando if — e não guard — é a escolha certa
Devemos trocar todo if por guard? Não. O critério é claro: use guard para uma pré-condição, “sem isto não dá para continuar”; use if para uma bifurcação, “neste caso faça isto, naquele faça aquilo”.
// ifO lugar certo para if — os dois caminhos são fluxos normais
if user.isPremium {
showPremiumBadge()
} else {
showUpgradeButton()
}
Não ser premium não é uma falha. Se os dois ramos continuam como cenários normais, use if. Escrever isso com guard daria o sinal errado de que não ser premium é anormal.
Por outro lado, se a falha em uma verificação encerra a função, guard é apropriado mesmo com uma única verificação. A escolha da sintaxe comunica significado. Quem lê espera que guard indique uma pré-condição e que if indique uma bifurcação; atender a essa expectativa é a essência da legibilidade.
Vale apontar um antipadrão: preencher o bloco else de guard com lógica. Quando começam ali tentativas de recuperação, mudanças de estado ou processamentos longos, quebra-se a promessa de que a falha termina rapidamente. Se else passar de três linhas, use isso como sinal para revisar o design. Um tratamento de falha tão complexo é uma bifurcação separada ou algo que deve ser lançado para o chamador.
guard em loops e código assíncrono
guard também é usado fora de funções. Dentro de loops, ele combina com continue para expressar “pular este item”.
for item in items {
guard item.isValid else { continue }
guard let url = item.downloadURL else { continue }
process(url)
}
Quando há várias condições de filtragem, guard mantém o corpo do for plano. Para condições simples, for item in items where item.isValid ou compactMap pode ser mais conciso. Se uma cláusula basta, use where; quando há desembrulho e várias etapas, guard é mais conveniente.
Em código assíncrono, combinar [weak self], familiar em closures, é praticamente um idioma.
fetchData { [weak self] data in
guard let self else { return }
self.update(with: data)
}
A pré-condição “não faça nada se self já tiver sido desalocado” é tratada na primeira linha, e o restante do código prossegue em um caminho plano onde self está disponível. É o ponto em que a filosofia de saída antecipada de guard encontra o gerenciamento de memória.
Resultado confirmado por execução direta
No Apple Swift 6.3.3, criei uma função que recebe String?, rejeita nil no else de guard e usa a string vinculada na linha seguinte quando há um valor.
guard=rejected,accepted:devpaw
Esse resultado coincide com meu critério para recomendar guard em uma revisão. Se uma entrada inválida encerra o caminho atual e apenas valores validados continuam no corpo principal, guard é adequado. Se verdadeiro e falso são fluxos normais de negócio, mantenho if em vez de trocá-lo apenas para parecer mais plano.
Resumo
- guard oferece saída antecipada no nível da linguagem e transforma a pirâmide do caos em uma lista de pré-condições seguida de um corpo principal plano.
- A diferença em relação a if é a garantia do compilador: else precisa sair do escopo, e os bindings permanecem válidos em todo o escopo posterior.
- Critério: use guard para pré-condições cuja falha impede o avanço e if para bifurcações em que os dois caminhos são normais. A própria sintaxe comunica significado.
- Um bloco else longo indica que guard está sendo usado incorretamente. Se o tratamento da falha for complexo, resolva-o com uma bifurcação ou lançando um erro.
A última frase mencionou “lançar um erro”. Esse é o assunto do próximo artigo: throws, as três formas de do-catch, try? e try!, além de Result — o mapa completo do tratamento de erros em Swift.
Leitura recomendada
- Padrão Decorator em Swift: como adicionar camadas de funcionalidade sem herança (exemplos e guia prático)
- [Swift básico #5] Mapa completo do tratamento de erros em Swift: quando usar throws, try?, try! e Result
- [Swift básico #6] Por que strings do Swift não aceitam text[0]? Guia completo sobre clusters de grafemas
Fontes e verificação
- The Swift Programming Language: Control FlowSwift.org · Documentação oficial · Consultado 26 de agosto de 2026Evidência: Saída antecipada de guard, escopo do binding de opcionais e legibilidade
- The Swift Programming Language: StatementsSwift.org · Padrão ou especificação · Consultado 26 de agosto de 2026Evidência: Regra sintática: guard else deve transferir o controle usando return, break, continue, throw ou Never

![Imagem de capa de [Swift básico #4] guard em Swift: achate a pirâmide do caos](/assets/images/posts/eb18b3e1-ed72-4816-890a-2865806e1441/1.jpg)