When storing the type in a variable and then using it as an argument in a generic function the compiler says it cannot convert it.
protocol Baz {
var x: String { get }
}
final class Foo: Baz {
var a: String
var b: String
var x: String
init() {
a = "a"
b = "b"
x = "x"
}
}
func stuff<A>(_ a: A.Type) where A: Baz {
print(A.self)
print(a.self)
}
let a: Baz.Type = Foo.self
// Generic parameter 'A' could not be inferred
// Cannot convert value of type 'Baz.Type' to expected argument type 'A.Type'
stuff(a)
// Works
stuff(Foo.self)
It's not clear to me why this is not allowed. Can someone help me understand?
You've erased the concrete type to the protocol type with Baz.Type = Foo.self and protocols don't conform to themselves, so you can no longer pass the value through the function which requires Baz conformance.
That makes sense. In order for me to be able to store that type and still be able to send it to stuff I need to put Foo in a Box.
struct Box<A: Baz> {
let type: A.Type
init(putInBox type: A.Type) {
self.type = type
}
}
let a = Box(putInBox: Foo.self)
stuff(a.type)
Tada!
The only problem with that is that the Box type is generic and "leaks" the type information. Are there other design patterns that allow me to "Box" it up and still pass it to stuff? Edit: I feel like an opaque return types might be the answer... but I'm not sure.
There are many approaches, all of which erase the original type to some extent. Some approaches capture implementation, perhaps through closures or values, some abstract the internals through a protocol, sort of like what you're already trying to do. There are many articles about it, here's one: Different flavors of type erasure in Swift | Swift by Sundell. For your Baz, with its one requirement, it's fairly easy:
struct AnyBaz: Baz {
var x: String
init<T: Baz>(_ aBaz: T) {
x = aBaz.x
}
}
For more complex protocols there are additional approaches.
One other approach I forgot to mention is, of course, using the protocol as the box itself. Just storing a collection of Baz values should work until you start using a protocol requirement, like Self or associatedtypes, that prevent it.