🧩

Swift 5.6 の any について & some との違い

2022/05/19に公開

any について

Swift 5.6 からanyキーワードが使用可能になりました。anyは存在型 (existential type) の前に置かれます。

Swift 5.6 ではanyを付けても付けなくてもOKですが、今後のSwiftバージョンでは付けないと警告が出るようになり、Swift 6 以降は必須になる予定です。

protocol Animal {
    func sleep()
}
struct Cat: Animal {
    func sleep() { print("zzz") }
}
let pet: Animal = Cat() // Swift 5.6より前
let pet: any Animal = Cat() // Swift 5.6以降
pet.sleep()

ここでのpetの型は"存在型" (existential type) と呼ばれます。プロトコルAnimalに準拠したものなら何でも保持できます。petに何が入るかの決定はコンパイル時(static dispatch)ではなく実行時(dynamic dispatch)になります。そのため、実行時に何が入るかわからずメモリ確保の面で非効率です。そこでanyを使ってそのデメリットを明示的にすることが可能になりました。新しいキーワードを作ってまでその非効率さを強調したいということで、よほどのことなのでしょう。可能な限りジェネリクスに置き換え可能かどうか検討した上で存在型を使いましょう。

ちなみに関数の引数や返却値の型に用いる場合は次のようになります。

func mofmof(animal: any Animal) {
    print("mof mof \(type(of: animal))")
}
mofmof(animal: Cat())
func randomAnimal() -> any Animal {
    Bool.random() ? Cat() : Dog()
}
randomAnimal()

ジェネリクスの場合

func mofmof<T: Animal>(animal: T) {
    print("mof mof \(type(of: animal))")
}
mofmof(animal: Cat())
func specificAnimal<T: Animal>() -> T? {
    Dog() as? T ?? Cat() as? T
}
let dog: Dog? = specificAnimal()
let cat: Cat? = specificAnimal()

some (Swift 5.1~) との違い

anysomeもプロトコル名の前に付き、指定したプロトコルに準拠した任意の型を受け付けるという点では似ています。
someは関数の返却値の型や計算型プロパティの型に使われ、外部に多くの複雑な型を公開したくないフレームワークやライブラリで活躍します。実行時に動的に型を決定するanyと違い、someはコンパイルチェックが働き、メモリのパフォーマンスに悪影響はありません。

var shiba: some Animal { Dog() }

func generateAnimal() -> some Animal {
    Cat()
}

実行時にランダム要素でCatDogを返す場合

// ⭕️こちらはOK
func randomAnimal1() -> any Animal {
    Bool.random() ? Dog() : Cat()
}

// ❌コンパイルエラー Result values in '? :' expression have mismatching types 'Dog' and 'Cat'
func randomAnimal2() -> some Animal {
    Bool.random() ? Dog() : Cat()
}

参考

🔗 What is the “any” keyword in Swift?
🔗 What is the “some” keyword in Swift?
🔗 What’s new in Swift 5.6?

Discussion