SwiftのOpaque Typeとは?

2月 16, 2025

Opaque Type(不透明型)は、Swift 5.1で導入された概念で、Opaque Return Type(不透明返却型)とも呼ばれます。 プロトコルやジェネリクス、特にAssociated Typeを持つプロトコルの使用を簡素化します。この記事では、Opaque Typeとは何か、そしてどのように使うのかを解説します。

プロトコルとジェネリクスの課題

Associated Typeを持つプロトコルを関数の戻り値型として使用する場合、具体的なデータ型を指定することが難しい場合があります。 例えば:

protocol P {
    associatedtype Value
    var value: Value { get } 
    init(value: Value) 
}

struct S1 : P { 
    var value: Int 
} 
struct S2: P { 
    var value: String 
}

// エラー: 戻り値の型を推測できません
// func foo() -> P { return S1() } 

ジェネリクスを使用する場合も、関数の引数や戻り値の型を明示的に指定する必要があるため、制限があります。

func foo<T: P>(value: T.Value) -> T { return T(value: value) }
let s1: S1 = foo(value: 10) 
let s2: S2 = foo(value: "ahihi")

Opaque Typeによる解決策

someキーワードで宣言されるOpaque Typeを使用すると、関数はプロトコルに準拠する「隠された」データ型を返すことができます。具体的な型を指定する必要はありません。

func foo() -> some P { return S1(value: 11) }

上記の例では、some PはプロトコルPを満たす具体的なデータ型を表し、コンパイラが自動的に推論します。

ただし、Opaque Typeは同じ関数内で1つのデータ型のみを返すことができます。

// エラー: 複数の異なる型を返すことはできません
// func bar(_ x: Int) -> some P {
//     if x > 10 { return S1(value: 12) } 
//     else { return S2(value: "ahihi") } 
// }

Opaque TypeとPATs (Protocols with Associated Types)

Opaque Typeは、PATsを扱う際に特に役立ちます。 具体的なデータ型を知らなくても、PATsを戻り値型として使用できるため、コードの柔軟性が高まり、ライブラリのバージョンに縛られることが少なくなります。

func giveMeACollection() -> some Collection { return [1, 2, 3] }
let collection = giveMeACollection()
print(collection.count) // 3

Opaque Typeと具体的な型

Opaque Typeを持つ関数が具体的な型を返す場合、コンパイラは、その関数への呼び出しが常に同じ型を返すことを保証します。

func fooo() -> some Equatable { return 5 }
let f1 = fooo() 
let f2 = fooo()
print(f1 == f2) // true

ただし、これはPATsには当てはまりません。 Opaque TypeとPATsを持つ各関数は、たとえ同じプロトコルに準拠していても、別々のデータ型を返します。

Opaque Typeとジェネリックプレースホルダー

Opaque Typeとジェネリックプレースホルダーを組み合わせることで、複数のデータ型に対応できる汎用的な関数を、具体的な型を返さずに作成できます。

なぜOpaque Typeを使うべきなのか?

Opaque Typeは、プロトコル、特にPATsを扱う際に柔軟性をもたらします。 具体的なデータ型を指定する必要がなくなり、ライブラリのバージョンへの依存を減らし、コードの再利用性を高めます。

SwiftUIにおけるOpaque Type

Opaque TypeはSwiftUIで広く使用されています。 Viewプロトコルのbodyプロパティはsome Viewという型を持ち、Viewプロトコルに準拠する任意のデータ型を返すことができます。 これにより、ユーザーインターフェースの構築が簡素化されます。 some Viewを使用することで、Viewが複数階層にネストされている場合に、複雑なデータ型を避けることができます。

Leave A Comment

Create your account