Post

Opaque Result Types

SE-0244 enables generic protocols to be used as return types.
(i.e. ones that have associated types or references to Self)

This means that given a protocol with an associated type, such as:

1
2
3
4
5
protocol SomeProtocol {
    associatedtype OtherType

    func someFunction() -> OtherType
}

… and implementations of SomeProtocol, that use different types for OtherType

1
2
3
4
5
6
7
struct A: SomeProtocol {
    func someFunction() -> Int { 1_234 }
}

struct B: SomeProtocol {
    func someFunction() -> String { "ABCD" }
}

… we can now use these implementations interchangebly as returned types for OtherType as long as we mark the return type in the function signature with the some keyword:

1
2
3
4
5
6
7
8
9
10
func foo() -> some SomeProtocol {
    A()
}

func bar() -> some SomeProtocol {
    B()
}

foo().someFunction() // 1234
bar().someFunction() // ABCD

Without the use of some the compiler would throw a much-disliked error that’s often seen when working with generic types:
Protocol 'SomeProtocol' can only be used as a generic constraint because it has Self or associated type requirements

Bigger picture

For SwiftUI this is one concept that powers the Result Builders behind VStack, Group, or any body property of a SwiftUI View to return different variants of the same protocol.
some View as a return type allows the use of any type that conforms to the generic protocol View.
May it be TupleView<(Text, Text)> or TupleView<(Text, Text, Text)>.

Future Plans

Just having some for result types doesn’t solve the Self or associated type requirements problem completely.

For example, if I try to create an array consisting of types conforming to a generic protocol, I’ll run into our old friend again:

1
2
3
4
5
6
7
8
let array = [A(), B()]
// Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional

let array: [SomeProtocol] = [A(), B()]
// Protocol 'SomeProtocol' can only be used as a generic constraint because it has Self or associated type requirements

let array: [some SomeProtocol] = [A(), B()]
// 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions

It seems that this will be solved by the inclusion of SE-0309: Unlock existentials for all protocols12 which will most likely be included in Swift 5.6.

This is also touched at in this great article by Tim Ekl by @timothyekl explaining the nature of the proposal.


This post is licensed under CC BY 4.0 by the author.