We saw in an earlier exercise that protocols can be used as types on some occasions.
let favoriteNumber: Int = 7 let length: Double = 423.4 let purchase: String = "chair" let describableThings: [CustomStringConvertible] = [5, "hello", [1,2,3]] for thing in describableThings { print(thing.description) } // prints, 5, "hello", [1,2,3]
Protocols can also be used as return types in functions:
func getDescribableThing() -> CustomStringConvertible { return Bool.random() ? "hello" : 3 } print(getDescribableThing()) // prints "hello" or 3
But when we try it with some protocols, we get an error!
func getHashbleThing() -> Hashable { return Bool.random() ? "hello" : 3 } // Error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements.
This doesn’t work because Hashable
has an associated type alongside it. Swift always needs to make sure it understands what all of the types will be, and allowing us to return a Hashable
thing could mean that Swift tries to use different associated type properties.
We can resolve this with Opaque Types. Opaque types allow us to hide the specific data type that is being returned from the caller. The caller doesn’t need to know the exact type being returned in this case, only that the function should return something that conforms to Hashable
.
To return an opaque type, we can use the some
keyword:
func getHashbleThing() -> some Hashable { return Bool.random() ? "hello" : 3 }
Closer, but this still won’t compile! That’s because if we use an opaque type, all of the underlying types have to be the same.
func getHashbleThing() -> some Hashable { return Bool.random() ? "hello" : "goodbye" }
Opaque types don’t let us return different types, but they do let us hide information from the caller. This can be useful if you want to keep certain elements of your code private so that callers won’t try to use elements in ways they shouldn’t.
Instructions
Write a method named getResettableCollection()
that returns some ResettableCollection
. In its implementation, define and return a ResettableStringArray
.
Define a variable collection
and assign it to getResettableCollection
using type inference.
Try to access a String
specific method on the first element in the collection
using the syntax:
collection.elements[0].lowercased
You should get a compiler error!
Comment out that line so that your code compiles again.
Reset all of the elements in your collection
, then print the collection
. You can’t access any of the underlying type methods and properties, but you can still use the methods from the protocol!