Em resumo: duas coisas que já se comportavam de forma diferente estavam reunidas em uma única sintaxe, e o Swift trouxe essa distinção à superfície. some View, any Error, some Collection. Antes, o nome do protocolo podia ser usado diretamente em posições de tipo, mas agora o compilador exige any. O que mudou?
A distinção entre polimorfismo estático e dinâmico, antecipada no artigo sobre genéricos, é exatamente essa. Na terceira parte da série intermediária, organizamos os conceitos de some e any.
Origem do problema — protocolos têm duas faces em posições de tipo
Protocolos são originalmente uma linguagem de restrições. Eles expressam qualificações, como um tipo que adota Comparable. Porém, quando o nome de um protocolo ocupa a posição de uma variável ou de um tipo de retorno, sua natureza muda.
let shapes: [Shape] = [Circle(), Square(), Triangle()]
Este array mistura tipos diferentes. Para tornar isso possível, o compilador coloca cada valor em uma caixa: uma caixa rotulada como “algo que segue Shape”. Isso é um existential type. O tipo concreto dentro da caixa só pode ser conhecido em tempo de execução, e as chamadas de método se tornam chamadas indiretas que abrem a caixa para encontrar a implementação real.
O problema é que essa caixa não é gratuita. Como valores de tamanhos diferentes precisam caber em uma caixa uniforme, existe um formato separado de existential container; valores grandes ficam no heap, e a caixa armazena apenas um ponteiro. As chamadas passam por uma tabela de funções chamada witness table, resultando em despacho dinâmico e bloqueando boa parte das otimizações do compilador, como inlining e especialização. Também há limitações funcionais. Como o tipo concreto foi apagado (type erasure), fica difícil responder se dois Shapes são do mesmo tipo.
A sintaxe antiga escondia esse custo. Escrever func draw(shape: Shape) cria uma caixa, enquanto escrever func draw<S: Shape>(shape: S) funciona com genéricos sem uma. Dois códigos aparentemente parecidos têm características de desempenho completamente diferentes, mas a sintaxe escondia essa diferença. Foi exatamente por isso que a proposta Swift Evolution SE-0335 introduziu a palavra-chave any: indicar explicitamente onde uma caixa é criada. É outro exemplo de como a filosofia de “não esconder custos”, vista na Parte 1, levou a uma mudança de sintaxe.
some — ocultar sem uma caixa
Se any é uma caixa que aceita qualquer coisa, some é a ferramenta na direção oposta. some Shape significa que o tipo concreto está definido como um único tipo, mas seu nome não será revelado. Por isso ele é chamado de opaque type.
func makeShape() -> some Shape {
Circle(radius: 10) // sempre Circle retorna apenas um tipo
}
O chamador não sabe o nome Circle, mas o compilador sabe. Por isso não há caixa nem chamada indireta. O despacho estático e as otimizações continuam disponíveis, então, na prática, ele é tratado como um genérico. O preço é a flexibilidade: uma função que retorna some Shape precisa retornar o mesmo tipo concreto em todos os caminhos de retorno. Retornar Circle ou Square dependendo de uma condição gera erro de compilação.
O var body: some View do SwiftUI é o uso mais representativo dessa sintaxe. O tipo que body realmente retorna é um monstro como VStack<TupleView<(Text, Image)>>, e você não pode nem quer colocá-lo na assinatura. some View resolve o problema ocultando apenas o nome, enquanto o compilador continua conhecendo o tipo concreto, sem abrir mão do desempenho. Essa é a verdadeira natureza da sintaxe que vimos em Progressive Disclosure como exemplo de “complexidade que iniciantes não precisam conhecer”.
Também vale conhecer o some na posição de parâmetro. func draw(shape: some Shape) é uma forma abreviada de func draw<S: Shape>(shape: S) (SE-0341). É uma sintaxe leve para usar genéricos quando o nome do parâmetro de tipo não é necessário no corpo.
Critério de escolha — some por padrão, any quando houver um motivo
A diferença entre as palavras-chave pode ser resumida assim: some fixa um tipo em tempo de compilação (despacho estático, otimização possível, relações entre tipos preservadas), enquanto any permite qualquer tipo em tempo de execução (despacho dinâmico, custo da caixa, apagamento de tipo).
O critério prático é claro: use some (ou genéricos) por padrão e use any apenas quando houver um motivo real para misturar vários tipos.
Há aproximadamente três usos legítimos para any. Primeiro, coleções heterogêneas. Colocar tipos diferentes no mesmo array, como em [any Shape], é impossível sem uma caixa. Segundo, propriedades armazenadas cujo tipo é decidido em tempo de execução, como var strategy: any PaymentStrategy, onde uma implementação diferente é conectada conforme a configuração. Propriedades de protocolo no padrão Strategy e na injeção de dependência geralmente entram aqui. Terceiro, funções cujo tipo de retorno muda conforme a condição—o caso que some não permite.
Em outras palavras, se você só quer que um parâmetro de função aceite qualquer tipo que siga este protocolo, a resposta é some. A cada chamada, o tipo é único. O guia da equipe do Swift segue a mesma direção: promova para any apenas quando for necessário misturar ou armazenar valores em uma coleção.
Não é preciso exagerar nem ignorar a diferença de desempenho. Em código que processa alguns eventos de UI, o custo de any é irrelevante. Mas, dentro de um loop executado dezenas de milhares de vezes por segundo, o custo da caixa e as otimizações bloqueadas podem gerar uma diferença mensurável. Como sempre, o critério é medir; usar some por padrão também reduz o que precisa ser medido.
Interpretando mensagens de erro — o que “adicione any” está dizendo
Quando você entende essa distinção, as mensagens do compilador que antes eram apenas decoradas para serem ignoradas passam a fazer sentido.
“Use of protocol ‘X’ as a type must be written ‘any X’” exige que a sintaxe reconheça que usar um protocolo como tipo cria uma caixa. Antes de adicionar any mecanicamente, pergunte se essa posição realmente precisa de uma caixa (ou se pode ser resolvida com some ou genéricos). Essa é a forma correta de lidar com esse erro.
“Protocol ‘X’ can only be used as a generic constraint” é o famoso erro do Swift antigo, exibido quando um protocolo com associatedtype ou Self era usado em uma posição de tipo. Isso significava que não era possível definir o formato da caixa porque o tipo associado era desconhecido. Hoje, o idioma permite muito mais com SE-0309, primary associated types e outros recursos, mas essa questão só é resolvida de verdade ao tratar dos tipos associados. Esse é o tema do próximo artigo.
Resumo
- Usar um protocolo em uma posição de tipo cria um existential type (uma caixa), junto com despacho dinâmico, custo do contêiner e apagamento de tipo. any é o rótulo honesto dessa caixa.
- some faz o oposto: fixa um tipo concreto e oculta apenas seu nome. Como não há caixa, o desempenho é equivalente ao de genéricos; some View do SwiftUI é o exemplo clássico.
- Use some (genéricos) por padrão e reserve any para situações em que vários tipos realmente se misturam, como coleções heterogêneas, armazenamento decidido em tempo de execução e retornos condicionais.
- Leia a exigência de any do compilador como um sinal para reconhecer o custo da caixa e, antes de adicioná-lo sempre, verifique se o caso pode usar some.
O próximo artigo é, como anunciado, sobre associatedtype: a origem do erro “generic constraint”, o significado de um protocolo conter um espaço reservado para tipo e os primary associated types.

![Imagem de capa de [Swift intermediário #3] some vs any e o custo dos existenciais](/assets/images/posts/a880d464-6c55-4a06-bf69-b6f45434a102/1.jpg)