I have this sort of piece of code:
public class Metal {}
public protocol Vehicle<M> {
associatedtype M
var material: [M] { get set }
}
public protocol Tracktor: Vehicle where M == Metal {}
public protocol MetalTracktor: Tracktor {
var material: [Metal] { get set }
}
Fails to compile the second definition of var material: [Meta]
with error:
Cannot override mutable property 'material' of type '[Self.M]' with covariant type '[Metal]'
Why? What's the right way to specialize Tracktor
with Metal
type?
I would think that Tracktor
is already specialized with Metal
type, thus the first and second definitions end up being the same and thus should not clash. What am I missing?
I understand that the second definition is redundant, but Swift usually allows redundant definitions. Why doesn't it in this case?
The Tracktor
protocol already specialises Vehicle
. You already did it.
You can write an implementation for Tracktor
, and use [Metal]
as the type of material
.
public class MyTracktor: Tracktor {
public var material: [Metal] = []
}
Swift first determines whether MyTracktor
conforms to Vehicle
. It infers that the associated type M
is Metal
. This implicitly creates a typealias
declaration:
typealias M = Metal
Now MyTracktor
satisfies both requirements of Vehicle
and so conforms to it. It is also the case that MyTracktor.M == Metal
, so it conforms to Tracktor
.
Writing var material: [Metal] { get set }
in MetalTracktor
is not allowed, because in the context of MetalTracktor
, M
and Metal
are two different (though related) types. M
is an associated type declared in Vehicle
, and Metal
is a class type declared at the global scope.
Compare this to the MyTracktor
class above. In MyTracktor
, M
is a type alias for Metal
- M
and Metal
are the same type. where M == Metal
on the other hand, is stating a requirement of Tracktor
, not a typealias.
Swift could technically be designed to be smarter about this, and allow you to write var material: [Metal] { get set }
in MetalTracktor
, but this is not very useful, since you can just write var material: [M] { get set }
to do basically the same thing.