Opaque result types are a useful tool for abstracting the return type of a function or subscript, or type of a property. Although the concrete underlying type of an opaque type is hidden from clients, it is still inferred by the compiler, which enforces certain usage requirements:
- Property declarations with opaque types must have an initializer expression or getter, and functions or subscripts returning opaque types must have at least one
return
statement:
let x: some Equatable // error: property declares an opaque return type, but has no initializer expression from which to infer an underlying type
let y: some Equatable = 42 // OK
let z: some Equatable { // Also OK
return "hello, " + "world!"
}
func foo() -> some Equatable { // error: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
fatalError("Unimplemented")
}
func bar() -> some Equatable { // OK
fatalError("Unimplemented")
return 42
}
- The underlying type of an opaque type must be unique. In other words, if a function or subscript returns an opaque type, it must return values of the same underlying type from every
return
statement in its body.
func foo(bar: Bool) -> some Equatable { // error: function declares an opaque return type, but the return statements in its body do not have matching underlying types
if bar {
return "hello, world!" // note: return statement has underlying type 'String'
} else {
return 1 // note: return statement has underlying type 'Int'
}
}
func bar(baz: Bool) -> some Equatable { // OK, both branches of the if statement return a value of the same underlying type, Int.
if baz {
return 100
} else {
return 200
}
}
- Functions returning opaque types may be recursive. However, such functions must have at least one
return
statement that returns a concrete underlying type as opposed to the function's own opaque result type. Additionally, recursive calls may not be used to create an infinitely recursive opaque type.
func foo(_ x: Int) -> some Equatable { // error: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
// Not allowed because there aren't any non-recursive returns to infer the underlying type from.
return foo(x+1)
}
struct EquatableWrapper<T: Equatable>: Equatable { var value: T }
func foo() -> some Equatable { // error: function opaque return type was inferred as 'EquatableWrapper<some Equatable>', which defines the opaque type in terms of itself
// Not allowed because the use of EquatableWrapper creates an infinitely recursive underlying type: EquatableWrapper<EquatableWrapper<EquatableWrapper<...>>>...>
return EquatableWrapper(value: foo())
}
func bar(_ x: Int) -> some Equatable { // OK, the underlying type can be inferred from the second return statement.
if x > 0 {
return bar(x-1)
} else {
return x
}
}
To learn more about opaque result types, see the Opaque Types section of The Swift Programming Language.