protocol Clock {}
struct SystemClock: Clock, Equatable {}
struct Timer<C: Clock & Equatable>: Equatable {
let clock: C
}
extension Timer where C == SystemClock {
init() {
self.clock = SystemClock()
}
}
let t = Timer<SystemClock>()
print(t)
The feature you'd want to make what you originally wrote be correct would be a default generic type. As others noted below, even a default wouldn't work in that context, since there's no guarantee C will be SystemClock .
is essentially the same as the issue in this code:
struct S<T: BinaryInteger> {
let value: T
init() {
self.value = UInt8(123) // ERROR: Cannot assign value of type 'UInt8' to type 'T'
}
}
or even simpler, in this code:
func foo<T: Comparable>() -> T {
return 123 // ERROR: Cannot convert return expression of type 'Int' to return type 'T'
}
The fundamental thing that you are missing is that the type parameter T (your C) can be any type that conforms to Comparable (your Clock), and you cannot write your generic code as if that type parameter is exactly Int (or SystemClock).
SystemClock conforms to Clock, UInt8 conforms to BinaryInteger, Int conforms to Comparable, but Int is not convertible to any type T conforming to Comparable.
I don't see why a default generic type would help. What @yxckjhasdkjh wrote originally doesn't work because, as the diagnostic says, SystemClock isn't C. This is because the user can choose any type for C that conforms to Clock & Equatable, whereas SystemClock is one specific type that does so. Even if you could assign a default choice for C, it doesn't change the fact that the user can choose a type that isn't SystemClock.
However, in an initializer I am explicitly controlling the kind of concrete type that I'm creating. So, in theory, the compiler would know enough to infer that Timer() always returns a Timer<SystemClock> (whereas I might have more specific additional initializers).
@nuclearace's approach works, but I wouldn't have discovered it by myself.
You are not in control of the concrete type, because it is specified by the user. If I write let t: Timer<SomeOtherClock> = .init(), you need to return me a Timer<SomeOtherClock>.