不透明型とボックス型
日本語を消す 英語を消す下記URLから引用し、日本語訳をつけてみました。
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/opaquetypes
Hide implementation details about a value’s type.
値の型に関する実装の詳細を非表示にします。
Swift provides two ways to hide details about a value’s type: opaque types and boxed protocol types. Hiding type information is useful at boundaries between a module and code that calls into the module, because the underlying type of the return value can remain private.
Swift は、値の型に関する詳細を非表示にする 2 つの方法、不透明型とボックス化されたプロトコル型を提供します。 型情報を非表示にすることは、戻り値の基になる型をプライベートのままにすることができるため、モジュールとモジュールを呼び出すコードとの間の境界で役立ちます。
A function or method that returns an opaque type hides its return value’s type information. Instead of providing a concrete type as the function’s return type, the return value is described in terms of the protocols it supports. Opaque types preserve type identity — the compiler has access to the type information, but clients of the module don’t.
不透明な型を返す関数またはメソッドは、戻り値の型情報を隠します。 関数の戻り値の型として具体的な型を提供する代わりに、戻り値はサポートするプロトコルの観点から記述されます。 不透明型は型の同一性を保持します。コンパイラーは型情報にアクセスできますが、モジュールのクライアントはアクセスできません。
A boxed protocol type can store an instance of any type that conforms to the given protocol. Boxed protocol types don’t preserve type identity — the value’s specific type isn’t known until runtime, and it can change over time as different values are stored.
ボックス化されたプロトコル 型には、指定されたプロトコルに準拠する任意の型のインスタンスを格納できます。 ボックス化されたプロトコル型は型の同一性を保持しません。値の特定の型は実行時までわかりません。異なる値が保存されると時間の経過とともに変化する可能性があります。
The Problem That Opaque Types Solve
不透明型が解決する問題
For example, suppose you’re writing a module that draws ASCII art shapes. The basic characteristic of an ASCII art shape is a draw()
function that returns the string representation of that shape, which you can use as the requirement for the Shape
protocol:
たとえば、ASCII アートの形状を描画するモジュールを作成しているとします。 ASCII アート シェイプの基本的な特徴は、そのシェイプの文字列表現を返すdraw()
関数です。これは、Shape
プロトコルの要件として使用できます。
protocol Shape {
func draw() -> String
}
struct Triangle: Shape {
var size: Int
func draw() -> String {
var result: [String] = []
for length in 1...size {
result.append(String(repeating: "*", count: length))
}
return result.joined(separator: "\n")
}
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***
You could use generics to implement operations like flipping a shape vertically, as shown in the code below. However, there’s an important limitation to this approach: The flipped result exposes the exact generic types that were used to create it.
以下のコードに示すように、ジェネリックスを使用して、図形を垂直に反転するなどの操作を実装できます。 ただし、このアプローチには重要な制限があります。反転された結果は、その作成に使用された正確なジェネリック型を公開します。
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *
This approach to defining a JoinedShape<T: Shape, U: Shape>
structure that joins two shapes together vertically, like the code below shows, results in types like JoinedShape<FlippedShape<Triangle>, Triangle>
from joining a flipped triangle with another triangle.
以下のコードに示すように、2 つの図形を垂直方向に結合する JoinedShape<T: Shape, U: Shape>
構造を定義するこのアプローチでは、反転した三角形を別の三角形と結合することで、JoinedShape<FlippedShape<Triangle>, Triangle>
のような型が生成されます。 。
struct JoinedShape<T: Shape, U: Shape>: Shape {
var top: T
var bottom: U
func draw() -> String {
return top.draw() + "\n" + bottom.draw()
}
}
let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
print(joinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
Exposing detailed information about the creation of a shape allows types that aren’t meant to be part of the ASCII art module’s public interface to leak out because of the need to state the full return type. The code inside the module could build up the same shape in a variety of ways, and other code outside the module that uses the shape shouldn’t have to account for the implementation details about the list of transformations. Wrapper types like JoinedShape
and FlippedShape
don’t matter to the module’s users, and they shouldn’t be visible. The module’s public interface consists of operations like joining and flipping a shape, and those operations return another Shape
value.
形状の作成に関する詳細な情報を公開すると、完全な戻り値の型を記述する必要があるため、ASCII アート モジュールのパブリック インターフェイスの一部であることを意図していない型が漏洩する可能性があります。 モジュール内のコードはさまざまな方法で同じ形状を構築できますが、その形状を使用するモジュール外の他のコードは、変換のリストに関する実装の詳細を考慮する必要はありません。 JoinedShape
や FlippedShape
などのラッパー 型は、モジュールのユーザーにとっては重要ではないため、表示されるべきではありません。 モジュールのパブリック インターフェースは、シェイプの結合や反転などの操作で構成されており、これらの操作は別の Shape
値を返します。
Returning an Opaque Type
不透明型を返す
You can think of an opaque type like being the reverse of a generic type. Generic types let the code that calls a function pick the type for that function’s parameters and return value in a way that’s abstracted away from the function implementation. For example, the function in the following code returns a type that depends on its caller:
不透明型はジェネリック型の逆のようなものと考えることができます。 ジェネリック型を使用すると、関数を呼び出すコードでその関数のパラメーターの型を選択し、関数の実装から抽象化された方法で値を返すことができます。 たとえば、次のコードの関数は、呼び出し元に応じた型を返します。
func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }
The code that calls max(_:_:)
chooses the values for x
and y
, and the type of those values determines the concrete type of T
. The calling code can use any type that conforms to the Comparable
protocol. The code inside the function is written in a general way so it can handle whatever type the caller provides. The implementation of max(_:_:)
uses only functionality that all Comparable
types share.
を呼び出すコードは、max(_:_:)
x
と y
の値を選択し、それらの値の型によって T
の具体的な型が決まります。呼び出し側のコードは、Comparable
プロトコルに準拠する任意の型を使用できます。 関数内のコードは一般的な方法で記述されているため、呼び出し元が提供する型をすべて処理できます。 max(_:_:)
の実装では、すべての Comparable
型が共有する機能のみが使用されます。
Those roles are reversed for a function with an opaque return type. An opaque type lets the function implementation pick the type for the value it returns in a way that’s abstracted away from the code that calls the function. For example, the function in the following example returns a trapezoid without exposing the underlying type of that shape.
これらの役割は、不透明な戻り値の型を持つ関数では逆になります。 不透明型を使用すると、関数実装は、関数を呼び出すコードから抽象化された方法で、関数が返す値の型を選択できます。 たとえば、次の例の関数は、その形状の基礎となる型を公開せずに、台形を返します。
struct Square: Shape {
var size: Int
func draw() -> String {
let line = String(repeating: "*", count: size)
let result = Array<String>(repeating: line, count: size)
return result.joined(separator: "\n")
}
}
func makeTrapezoid() -> some Shape {
let top = Triangle(size: 2)
let middle = Square(size: 2)
let bottom = FlippedShape(shape: top)
let trapezoid = JoinedShape(
top: top,
bottom: JoinedShape(top: middle, bottom: bottom)
)
return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
// *
// **
// **
// **
// **
// *
The makeTrapezoid()
function in this example declares its return type as some Shape
; as a result, the function returns a value of some given type that conforms to the Shape
protocol, without specifying any particular concrete type. Writing makeTrapezoid()
this way lets it express the fundamental aspect of its public interface — the value it returns is a shape — without making the specific types that the shape is made from a part of its public interface. This implementation uses two triangles and a square, but the function could be rewritten to draw a trapezoid in a variety of other ways without changing its return type.
この例の makeTrapezoid()
関数は、戻り値の型を何らかの Shape
として宣言します。 その結果、関数は特定の具体的な型を指定せずに、Shape
プロトコルに準拠する特定の型の値を返します。 この方法で makeTrapezoid()
を記述すると、パブリック インターフェースの一部からシェイプが作成される特定の型を作成することなく、パブリック インターフェースの基本的な側面 (返される値はシェイプ) を表現できます。 この実装では 2 つの三角形と 1 つの正方形を使用しますが、戻り値の型を変更せずに、他のさまざまな方法で台形を描画するように関数を書き直すことができます。
This example highlights the way that an opaque return type is like the reverse of a generic type. The code inside makeTrapezoid()
can return any type it needs to, as long as that type conforms to the Shape
protocol, like the calling code does for a generic function. The code that calls the function needs to be written in a general way, like the implementation of a generic function, so that it can work with any Shape
value that’s returned by makeTrapezoid()
.
この例では、不透明な戻り値型がジェネリック型の逆のようなものであることを強調しています。 makeTrapezoid()
内のコードは、汎用関数の呼び出しコードと同様に、その型が Shape
プロトコルに準拠している限り、必要な任意の型を返すことができます。 関数を呼び出すコードは、makeTrapezoid()
によって返される任意の Shape
値を処理できるように、汎用関数の実装などの一般的な方法で記述する必要があります。
You can also combine opaque return types with generics. The functions in the following code both return a value of some type that conforms to the Shape
protocol.
不透明な戻り値の型とジェネリックを組み合わせることもできます。 次のコードの関数はどちらも、Shape
プロトコルに準拠する何らかの型の値を返します。
func flip<T: Shape>(_ shape: T) -> some Shape {
return FlippedShape(shape: shape)
}
func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape {
JoinedShape(top: top, bottom: bottom)
}
let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
print(opaqueJoinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
The value of opaqueJoinedTriangles
in this example is the same as joinedTriangles
in the generics example in the The Problem That Opaque Types Solve section earlier in this chapter. However, unlike the value in that example, flip(_:)
and join(_:_:)
wrap the underlying types that the generic shape operations return in an opaque return type, which prevents those types from being visible. Both functions are generic because the types they rely on are generic, and the type parameters to the function pass along the type information needed by FlippedShape
and JoinedShape
.
この例の opaqueJoinedTriangles
の値は、この章の前半の不透明型が解決する問題セクションのジェネリックスの例の joinedTriangles
と同じです。 ただし、この例の値とは異なり、flip(_:)
とjoin(_:_:)
は、汎用形状演算が返す基になる型を不透明な戻り値の型でラップするため、これらの型は表示されなくなります。 どちらの関数も、依存する型がジェネリックであるためジェネリックであり、関数の型パラメータは FlippedShape
と JoinedShape
に必要な型情報を渡します。
If a function with an opaque return type returns from multiple places, all of the possible return values must have the same type. For a generic function, that return type can use the function’s generic type parameters, but it must still be a single type. For example, here’s an invalid version of the shape-flipping function that includes a special case for squares:
不透明な戻り値を持つ関数が複数の場所から戻る場合、考えられる戻り値はすべて同じ型でなければなりません。 ジェネリック関数の場合、戻り値の型は関数のジェネリック型パラメーターを使用できますが、それでも単一の型である必要があります。 たとえば、正方形の特殊なケースを含む、形状反転関数の無効なバージョンを次に示します。
func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
if shape is Square {
return shape // Error: return types don't match
}
return FlippedShape(shape: shape) // Error: return types don't match
}
If you call this function with a Square
, it returns a Square
; otherwise, it returns a FlippedShape
. This violates the requirement to return values of only one type and makes invalidFlip(_:)
invalid code. One way to fix invalidFlip(_:)
is to move the special case for squares into the implementation of FlippedShape
, which lets this function always return a FlippedShape
value:
Square
を指定してこの関数を呼び出すと、Square
が返されます。 それ以外の場合は、FlippedShape
を返します。 これは、1 つの型のみの値を返すという要件に違反し、invalidFlip(_:)
が無効なコードになります。 invalidFlip(_:)
を修正する 1 つの方法は、正方形の特殊なケースを FlippedShape
の実装に移動することです。これにより、この関数は常に FlippedShape
値を返すようになります。
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
if shape is Square {
return shape.draw()
}
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
The requirement to always return a single type doesn’t prevent you from using generics in an opaque return type. Here’s an example of a function that incorporates its type parameter into the underlying type of the value it returns:
常に単一の型を返すという要件は、不透明な戻り型でジェネリックスを使用することを妨げるものではありません。 以下は、戻り値の基になる型に型パラメーターを組み込む関数の例です。
func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
return Array<T>(repeating: shape, count: count)
}
In this case, the underlying type of the return value varies depending on T
: Whatever shape is passed it, repeat(shape:count:)
creates and returns an array of that shape. Nevertheless, the return value always has the same underlying type of [T]
, so it follows the requirement that functions with opaque return types must return values of only a single type.
この場合、戻り値の基になる型は T
に応じて異なります。どのような形状が渡されたとしても、repeat(shape:count:)
はその形状の配列を作成して返します。 それにもかかわらず、戻り値は常に同じ基底型 [T]
を持ちます。そのため、不透明な戻り値を持つ関数は単一の型の値のみを返さなければならないという要件に従います。
Boxed Protocol Types
ボックス化されたプロトコル型
A boxed protocol type is also sometimes called an existential type, which comes from the phrase “there exists a type T such that T conforms to the protocol”. To make a boxed protocol type, write any
before the name of a protocol. Here’s an example:
ボックス化されたプロトコル 型は、実在型と呼ばれることもあります。これは、「プロトコルに準拠するような型T が実在する」というフレーズに由来しています。 ボックス化されたプロトコル 型を作成するには、プロトコル名の前に「any
」を記述します。 以下に例を示します。
struct VerticalShapes: Shape {
var shapes: [any Shape]
func draw() -> String {
return shapes.map { $0.draw() }.joined(separator: "\n\n")
}
}
let largeTriangle = Triangle(size: 5)
let largeSquare = Square(size: 5)
let vertical = VerticalShapes(shapes: [largeTriangle, largeSquare])
print(vertical.draw())
In the example above, VerticalShapes
declares the type of shapes
as [any Shape]
— an array of boxed Shape
elements. Each element in the array can be a different type, and each of those types must conform to the Shape
protocol. To support this runtime flexibility, Swift adds a level of indirection when necessary — this indirection is called a box, and it has a performance cost.
上の例では、VerticalShapes
はshapes
の型を [any Shape]
、つまりボックス化されたShape
要素の配列として宣言します。 配列内の各要素は異なる型にすることができ、それらの型はそれぞれ Shape
プロトコルに準拠する必要があります。 この実行時の柔軟性をサポートするために、Swift は必要に応じて間接化のレベルを追加します。この間接化はボックスと呼ばれ、パフォーマンス コストがかかります。
Within the VerticalShapes
type, the code can use methods, properties, and subscripts that are required by the Shape
protocol. For example, the draw()
method of VerticalShapes
calls the draw()
method on each element of the array. This method is available because Shape
requires a draw()
method. In contrast, trying to access the size
property of the triangle, or any other properties or methods that aren’t required by Shape
, produces an error.
VerticalShapes
型では、コードは Shape
プロトコルで必要なメソッド、プロパティ、添字を使用できます。 たとえば、VerticalShapes
のdraw()
メソッドは、配列の各要素に対してdraw()
メソッドを呼び出します。 Shape
にはdraw()
メソッドが必要なため、このメソッドが利用可能です。 対照的に、三角形のsize
プロパティ、または Shape
に必要のないその他のプロパティやメソッドにアクセスしようとすると、エラーが発生します。
Contrast the three types you could use for shapes
:
形状に使用できる 3 つの型を比較してください。
- Using generics, by writing
struct VerticalShapes<S: Shape>
andvar shapes: [S]
, makes an array whose elements are some specific shape type, and where the identity of that specific type is visible to any code that interacts with the array. struct VerticalShapes<S: Shape>
とvar shapes: [S]
を記述して、ジェネリックスを使用すると、特定のShape
型で、特定の型の同一性が呼応するすべてのコードで可視化できる要素を格納できる配列を作成します。- Using an opaque type, by writing
var shapes: [some Shape]
, makes an array whose elements are some specific shape type, and where that specific type’s identity is hidden. var shapes: [some Shape]
と記述して、不透明型を使用すると、特定のShape
型で、その特定のShape
型の同一性は不明な要素を格納できる配列を作成します。。- Using a boxed protocol type, by writing
var shapes: [any Shape]
, makes an array that can store elements of different types, and where those types’ identities are hidden. var shapes: [any Shape]
と記述して、ボックス化されたプロトコル 型を使用すると、異なる型の要素で、それらの型の同一性 が不明な要素を格納できる配列を作成します。
In this case, a boxed protocol type is the only approach that lets callers of VerticalShapes
mix different kinds of shapes together.
この場合、ボックス化されたプロトコル 型は、VerticalShapes
の呼び出し元がさまざまな種類のShape
を混在させることができる唯一のアプローチです。
You can use an as
cast when you know the underlying type of a boxed value. For example:
ボックス化された値の基礎となる型がわかっている場合は、as
キャストを使用できます。 例えば:
if let downcastTriangle = vertical.shapes[0] as? Triangle {
print(downcastTriangle.size)
}
// Prints "5"
For more information, see Downcasting.
詳細については、「ダウンキャスト」を参照してください。
Differences Between Opaque Types and Boxed Protocol Types
不透明型とボックス化プロトコル型の違い
Returning an opaque type looks very similar to using a boxed protocol type as the return type of a function, but these two kinds of return type differ in whether they preserve type identity. An opaque type refers to one specific type, although the caller of the function isn’t able to see which type; a boxed protocol type can refer to any type that conforms to the protocol. Generally speaking, boxed protocol types give you more flexibility about the underlying types of the values they store, and opaque types let you make stronger guarantees about those underlying types.
不透明型を返すことは、ボックス化されたプロトコル型を関数の戻り型として使用することに非常に似ていますが、これら 2 種類の戻り型は、型の同一性を保持するかどうかが異なります。 不透明型は 1 つの特定の型を指しますが、関数の呼び出し時はどの型かを認識することができません。 ボックス化されたプロトコル タイプは、プロトコルに準拠する任意のタイプを参照できます。 一般に、ボックス化されたプロトコル型では、格納される値の基になる型についてより柔軟な対応が可能になり、不透明型を使用すると、それらの基になる型についてより強力な保証が可能になります。
For example, here’s a version of flip(_:)
that uses a boxed protocol type as its return type instead of an opaque return type:
たとえば、以下は、不透明な戻り値の型ではなく、ボックス化されたプロトコル型を戻り値の型として使用する flip(_:)
のバージョンです。
func protoFlip<T: Shape>(_ shape: T) -> Shape {
return FlippedShape(shape: shape)
}
This version of protoFlip(_:)
has the same body as flip(_:)
, and it always returns a value of the same type. Unlike flip(_:)
, the value that protoFlip(_:)
returns isn’t required to always have the same type — it just has to conform to the Shape
protocol. Put another way, protoFlip(_:)
makes a much looser API contract with its caller than flip(_:)
makes. It reserves the flexibility to return values of multiple types:
このバージョンの protoFlip(_:)
は、 flip(_:)
と同じ本体を持ち、常に同じ型の値を返します。 flip(_:)
とは異なり、 protoFlip(_:)
が返す値は常に同じ型である必要はなく、Shape
プロトコルに準拠する必要があるだけです。 別の言い方をすると、protoFlip(_:)
は、flip(_:)
よりもはるかに緩やかな API コントラクトを呼び出し元と結びます。 複数の型の値を返す柔軟性が確保されています。
func protoFlip<T: Shape>(_ shape: T) -> Shape {
if shape is Square {
return shape
}
return FlippedShape(shape: shape)
}
The revised version of the code returns an instance of Square
or an instance of FlippedShape
, depending on what shape is passed in. Two flipped shapes returned by this function might have completely different types. Other valid versions of this function could return values of different types when flipping multiple instances of the same shape. The less specific return type information from protoFlip(_:)
means that many operations that depend on type information aren’t available on the returned value. For example, it’s not possible to write an ==
operator comparing results returned by this function.
コードの改訂版は、渡されたshapeに応じて、Square
のインスタンスまたは FlippedShape
のインスタンスを返します。この関数によって返される 2 つの反転された形状は、まったく異なるタイプを持つ可能性があります。 この関数の他の有効なバージョンでは、同じ形状の複数のインスタンスを反転するときに、異なる型の値を返す可能性があります。 protoFlip(_:)
からの戻り値の型情報があまり具体的ではないということは、型情報に依存する多くの操作が戻り値に対して利用できないことを意味します。 たとえば、この関数によって返された結果を比較する ==
演算子を作成することはできません。
let protoFlippedTriangle = protoFlip(smallTriangle)
let sameThing = protoFlip(smallTriangle)
protoFlippedTriangle == sameThing // Error
The error on the last line of the example occurs for several reasons. The immediate issue is that the Shape
doesn’t include an ==
operator as part of its protocol requirements. If you try adding one, the next issue you’ll encounter is that the ==
operator needs to know the types of its left-hand and right-hand arguments. This sort of operator usually takes arguments of type Self
, matching whatever concrete type adopts the protocol, but adding a Self
requirement to the protocol doesn’t allow for the type erasure that happens when you use the protocol as a type.
例の最後の行のエラーは、いくつかの理由で発生します。 当面の問題は、Shape
にプロトコル要件の一部として ==
演算子が含まれていないことです。 これを追加しようとすると、次に遭遇する問題は、==
演算子がその左側と右側の引数の型を知る必要があるということです。 この種の演算子は通常、プロトコルを採用する具体的な型に一致する Self
型の引数を受け取りますが、プロトコルに Self
要件を追加すると、プロトコルを型として使用するときに発生する型の消去が許可されません。
Using a boxed protocol type as the return type for a function gives you the flexibility to return any type that conforms to the protocol. However, the cost of that flexibility is that some operations aren’t possible on the returned values. The example shows how the ==
operator isn’t available — it depends on specific type information that isn’t preserved by using a boxed protocol type.
ボックス化されたプロトコル型を関数の戻り値の型として使用すると、プロトコルに準拠する任意の型を柔軟に返すことができます。 ただし、その柔軟性の代償として、戻り値に対して一部の操作が不可能になります。 この例は、==
演算子がどのように利用できないかを示しています。これは、ボックス化されたプロトコル 型を使用しても保持されない特定の型情報に依存しています。
Another problem with this approach is that the shape transformations don’t nest. The result of flipping a triangle is a value of type Shape
, and the protoFlip(_:)
function takes an argument of some type that conforms to the Shape
protocol. However, a value of a boxed protocol type doesn’t conform to that protocol; the value returned by protoFlip(_:)
doesn’t conform to Shape
. This means code like protoFlip(protoFlip(smallTriangle))
that applies multiple transformations is invalid because the flipped shape isn’t a valid argument to protoFlip(_:)
.
このアプローチのもう 1 つの問題は、shape変換がネストされないことです。 三角形を反転した結果は、Shape
型の値であり、protoFlip(_:)
関数は、Shape
プロトコルに準拠する何らかの型の引数を受け取ります。 ただし、ボックス化されたプロトコル 型の値はそのプロトコルに準拠しません。 protoFlip(_:)
によって返される値は Shape
に準拠していません。 これは、反転した形状が protoFlip(_:)
の有効な引数ではないため、複数の変換を適用する protoFlip(protoFlip(smallTriangle))
のようなコードは無効であることを意味します。
In contrast, opaque types preserve the identity of the underlying type. Swift can infer associated types, which lets you use an opaque return value in places where a boxed protocol type can’t be used as a return value. For example, here’s a version of the Container
protocol from Generics:
対照的に、不透明型は基になる型のアイデンティティを保持します。 Swift は関連する型を推論できるため、ボックス化されたプロトコル型を戻り値として使用できない場所で不透明な戻り値を使用できます。 たとえば、汎用の Container
プロトコルのバージョンは次のとおりです。
protocol Container {
associatedtype Item
var count: Int { get }
subscript(i: Int) -> Item { get }
}
extension Array: Container { }
You can’t use Container
as the return type of a function because that protocol has an associated type. You also can’t use it as constraint in a generic return type because there isn’t enough information outside the function body to infer what the generic type needs to be.
プロトコルには関連付けられた型があるため、関数の戻り値の型として Container
を使用することはできません。 また、ジェネリック型がどのようなものであるかを推測するのに十分な情報が関数本体の外部にないため、ジェネリック戻り型の制約として使用することもできません。
// Error: Protocol with associated types can't be used as a return type.
func makeProtocolContainer<T>(item: T) -> Container {
return [item]
}
// Error: Not enough information to infer C.
func makeProtocolContainer<T, C: Container>(item: T) -> C {
return [item]
}
Using the opaque type some Container
as a return type expresses the desired API contract — the function returns a container, but declines to specify the container’s type:
戻り値の型として不透明型の some Container
を使用すると、目的の API コントラクトが表現されます。関数はコンテナを返しますが、コンテナの型の指定を拒否します。
func makeOpaqueContainer<T>(item: T) -> some Container {
return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// Prints "Int"
The type of twelve
is inferred to be Int
, which illustrates the fact that type inference works with opaque types. In the implementation of makeOpaqueContainer(item:)
, the underlying type of the opaque container is [T]
. In this case, T
is Int
, so the return value is an array of integers and the Item
associated type is inferred to be Int
. The subscript on Container
returns Item
, which means that the type of twelve
is also inferred to be Int
.
twelve
の型は Int
であると推論されます。これは、型推論が不透明な型で機能するという事実を示しています。 makeOpaqueContainer(item:)
の実装では、不透明コンテナの基礎となるタイプは [T]
です。 この場合、T
は Int
であるため、戻り値は整数の配列となり、Item
に関連付けられた型は Int
であると推測されます。 Container
の添え字は、Item
を返します。これは、twelve
の型も Int
であると推測されることを意味します。