|
| 1 | +# Underlying Type Inference for Opaque Result Types |
| 2 | + |
| 3 | +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: |
| 4 | + |
| 5 | +- 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: |
| 6 | + |
| 7 | +```swift |
| 8 | +let x: some Equatable // error: property declares an opaque return type, but has no initializer expression from which to infer an underlying type |
| 9 | +let y: some Equatable = 42 // OK |
| 10 | +let z: some Equatable { // Also OK |
| 11 | + return "hello, " + "world!" |
| 12 | +} |
| 13 | + |
| 14 | +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 |
| 15 | + fatalError("Unimplemented") |
| 16 | +} |
| 17 | + |
| 18 | +func bar() -> some Equatable { // OK |
| 19 | + fatalError("Unimplemented") |
| 20 | + return 42 |
| 21 | +} |
| 22 | +``` |
| 23 | + |
| 24 | +- 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. |
| 25 | + |
| 26 | +```swift |
| 27 | +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 |
| 28 | + if bar { |
| 29 | + return "hello, world!" // note: return statement has underlying type 'String' |
| 30 | + } else { |
| 31 | + return 1 // note: return statement has underlying type 'Int' |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +func bar(baz: Bool) -> some Equatable { // OK, both branches of the if statement return a value of the same underlying type, Int. |
| 36 | + if baz { |
| 37 | + return 100 |
| 38 | + } else { |
| 39 | + return 200 |
| 40 | + } |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +- 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. |
| 45 | + |
| 46 | +```swift |
| 47 | +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 |
| 48 | + // Not allowed because there aren't any non-recursive returns to infer the underlying type from. |
| 49 | + return foo(x+1) |
| 50 | +} |
| 51 | + |
| 52 | +struct EquatableWrapper<T: Equatable>: Equatable { var value: T } |
| 53 | +func foo() -> some Equatable { // error: function opaque return type was inferred as 'EquatableWrapper<some Equatable>', which defines the opaque type in terms of itself |
| 54 | + // Not allowed because the use of EquatableWrapper creates an infinitely recursive underlying type: EquatableWrapper<EquatableWrapper<EquatableWrapper<...>>>...> |
| 55 | + return EquatableWrapper(value: foo()) |
| 56 | +} |
| 57 | + |
| 58 | +func bar(_ x: Int) -> some Equatable { // OK, the underlying type can be inferred from the second return statement. |
| 59 | + if x > 0 { |
| 60 | + return bar(x-1) |
| 61 | + } else { |
| 62 | + return x |
| 63 | + } |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +To learn more about opaque result types, see the [Opaque Types](https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html) section of _The Swift Programming Language_. |
| 68 | + |
0 commit comments