📌

【Swift】someとanyの違いを「someone」と「anyone」で理解する

に公開

挨拶

はいどうも〜、つんつん兄弟です!
初のテックブログなので緊張?してますが書いていきます!

はじめに

Swift 5.6以降、someany といったキーワードを記事や書籍などで目にする機会が急激に増えましたね。特にSwiftUIを書いていると var body: some View はお馴染みですが、最近では any Protocol という書き方も推奨されています。

「どっちもプロトコルを扱うものだけど、何が違うの?」
「結局、どっちを使えばいいの?」

そんな疑問を持つエンジニアのために、今回はこの2つのキーワード(someany)の違いについて、英語の「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のセッションでも言及されていますが、基本的には以下の優先順位で考えましょう。

  1. デフォルトは some を使う
    • パフォーマンスが良く、型安全性も高いため。
  2. 必要な場合のみ any を使う
    • 異なる型を同じ配列に入れたい場合。
    • 変数の型として保持し、後から別の型を代入したい場合。

最後に

someany は似て非なるものです。

  • some (someone): 特定の1つの型。静的。速い。
  • any (anyone): 色々な型を受け入れる箱。動的。柔軟だが少しコストがかかる。

これらを適切に使い分けることで、コンパイラに優しく、かつ意図が明確なSwiftコードを書くことができるでしょう。まずは「基本は some、混在させたいなら any」と覚えておきましょう!

参考文献

正確な仕様や詳細は、以下のApple公式ドキュメントも併せてご確認ください。

SKIYAKI Tech Blog

Discussion