Hey everyone,
I ran into an unusual situation and I’m wondering why it is the way it is and what’s the best practice for this case.
Say I have a factory function that spits out concrete types when requested a protocol:
func get<T>() -> T?
And say I have these definitions and a way to call the function:
protocol Something {}
class SomethingImpl: Something {}
let result: Something? = get()
I found that I can implement it using if
statements like this:
func get<T>() -> T? {
if T.self == Something.self {
return SomethingImpl() as? T
}
return nil
}
But using a switch in a seemingly similar fashion doesn’t compile:
func get<T>() -> T? {
switch T.self {
case Something.self: // Type of expression is ambiguous without a type annotation
return SomethingImpl() as? T
default:
return nil
}
}
The compiler error suggests that I need to specify the type of Something.self
more precisely but I’m not sure how I could? Is that a case where the compiler error is inaccurate?
Looking at older code I found that using case is
is one way to do it and it works for concrete types but it silently fails for protocols.
func get<T>() -> T? {
switch T.self {
case is Something.Type: // Never matches
return SomethingImpl() as? T
case is Int.Type:
return 123 as? T
default:
return nil
}
}
let something: Something? = get() // nil
let int: Int? = get() // Optional(123)
I did end up finding a solution that works in a way that’s closer to my first intuition by using ObjectIdentifier, but it feels a bit like doing the languages’ job, is it?
func get<T>() -> T? {
switch ObjectIdentifier(T.self) {
case ObjectIdentifier(Something.self):
return SomethingImpl() as? T
default:
return nil
}
}