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.