【Swift】someとanyの違いを「someone」と「anyone」で理解する
挨拶
はいどうも〜、つんつん兄弟です!
初のテックブログなので緊張?してますが書いていきます!
はじめに
Swift 5.6以降、some や any といったキーワードを記事や書籍などで目にする機会が急激に増えましたね。特にSwiftUIを書いていると var body: some View はお馴染みですが、最近では any Protocol という書き方も推奨されています。
「どっちもプロトコルを扱うものだけど、何が違うの?」
「結局、どっちを使えばいいの?」
そんな疑問を持つエンジニアのために、今回はこの2つのキーワード(someとany)の違いについて、英語の「someone(特定のだれか)」と「anyone(だれでも)」という単語のイメージを使って解説していきます。
レクチャー
まずは、それぞれのキーワードが持つ役割と、その裏側で起きていること(パフォーマンスへの影響)を整理します。
1. some:Opaque Type(不透明型)
some は、「外から見ると型は隠されているが、コンパイラは具体的な型を知っている」状態を作ります。
-
イメージ: someone (特定の誰か)
- 「誰か(someone)が来るよ」と言いつつ、実は「佐藤さん」という特定の人(型)が来ることが決まっています。
-
技術的定義: ジェネリクスのシンタックスシュガーとしての側面が強いです。
-
戻り値の場合: 内部では具体的な型(例:
Text)を返しますが、外部には「Viewに準拠した何か」とだけ伝えます。 -
引数の場合:
func method<T: Protocol>(val: T)と書くのとほぼ同義です。
-
戻り値の場合: 内部では具体的な型(例:
-
パフォーマンス:
- 静的ディスパッチ: コンパイル時に具体的な型が確定するため、最適化が効きやすく高速です。
2. any:Existential Type(存在型)
any は、「そのプロトコルに準拠してさえいれば、どんな型でも入る箱」を作ります。
-
イメージ: anyone (誰でも)
- 「誰でも(anyone)いいよ」という受付窓口のようなものです。佐藤さんでも鈴木さんでも、条件(プロトコル)さえ満たしていれば受け入れます。
-
技術的定義: 型としてのプロトコル(Existential Type)であることを明示します。
- 実行時に異なる型を代入し直すことができます。
-
パフォーマンス:
- 動的ディスパッチ: 具体的な型を隠蔽する「Existential Container(存在コンテナ)」という箱に包まれるため、メソッド呼び出し時にテーブル(Witness Table)を経由する必要があり、わずかにオーバーヘッドが発生します。
- メモリ: 具体的な型を箱に入れるためのメモリ確保が必要です(大きな型の場合はヒープ領域を使うこともあります)。
実践
では、コードを使って具体的な挙動の違いを見ていきましょう。
1. 宣言の簡略化(some)
引数において、some はジェネリクスの記述を非常にスッキリさせてくれます。以下の2つはほぼ同じ意味です。
protocol Entity {}
// 従来のジェネリクス
func map<E: Entity>(entity: E) {
// ...
}
// Swift 5.7以降: someを使った書き方
func map(entity: some Entity) {
// ...
}
2. 明示的なプロトコル型(any)
Swift 6に向けて、プロトコルを型として使う場合は any をつけることが推奨(将来的には必須化)されています。
protocol Menu {}
// 昔の書き方(Swift 6以降は警告またはエラーになる可能性あり)
func cook(menu: Menu) {}
// 今の推奨される書き方
func cook(menu: any Menu) {}
3. 【重要】配列での挙動の違い
ここが最大の使い分けポイントです。「同じ型で揃える必要があるか」どうかが鍵になります。
以下のようなプロトコルと構造体があるとします。
protocol Menu {}
struct Hamburgers: Menu {}
struct FriedChicken: Menu {}
someの場合:型の一貫性が必要
some は「特定の誰か」でなければならないため、配列の中身も全て同じ型である必要があります。
// [some Menu] は「特定のMenu準拠型」の配列という意味
func order(menus: [some Menu]) {
print("注文を受け付けました")
}
// ✅ OK: 全部 Hamburgers なので型が特定できる
order(menus: [Hamburgers(), Hamburgers()])
// ❌ コンパイルエラー:
// HamburgersとFriedChickenが混ざっており、型を1つに特定できない
order(menus: [Hamburgers(), FriedChicken()])
anyの場合:型の混合が可能
any は「誰でも受け入れる箱」の配列なので、中身の型がバラバラでも構いません。
// [any Menu] は「Menu準拠ならなんでも入る箱」の配列
func order2(menus: [any Menu]) {
print("いろいろな注文を受け付けました")
}
// ✅ OK: 型が混在していても、それぞれが box 化されて格納される
order2(menus: [Hamburgers(), FriedChicken()])
使い分けの指針
Appleのセッションでも言及されていますが、基本的には以下の優先順位で考えましょう。
-
デフォルトは
someを使う- パフォーマンスが良く、型安全性も高いため。
-
必要な場合のみ
anyを使う- 異なる型を同じ配列に入れたい場合。
- 変数の型として保持し、後から別の型を代入したい場合。
最後に
some と any は似て非なるものです。
- some (someone): 特定の1つの型。静的。速い。
- any (anyone): 色々な型を受け入れる箱。動的。柔軟だが少しコストがかかる。
これらを適切に使い分けることで、コンパイラに優しく、かつ意図が明確なSwiftコードを書くことができるでしょう。まずは「基本は some、混在させたいなら any」と覚えておきましょう!
参考文献
正確な仕様や詳細は、以下のApple公式ドキュメントも併せてご確認ください。
株式会社SKIYAKIのテックブログです。ファンクラブプラットフォームBitfanの開発・運用にまつわる知見や調べたことなどを発信します。主な技術スタックは Ruby on Rails / React / AWS / Swift / Kotlin などです。 recruit.skiyaki.com/
Discussion