I have the following code:
protocol Step { /* body */ }
enum StepA: Int, CaseIterable, Step {
case one
case two
case three
}
enum StepB: Int, CaseIterable, Step {
case one
case two
case three
}
protocol Component {
var step: Step { get }
}
protocol ComponentA: Component {
var step: StepA { get }
}
protocol ComponentB: Component {
var step: StepB { get }
}
struct SomeComponentOfTypeA: ComponentA {
var step: StepA = StepA.one
}
let a = SomeComponentOfTypeA()
print(a.step)
The type of a.step I would like to be StepA, since I am initalizing this way. But, I can't do that because:
struct SomeComponentOfTypeA: ComponentA {
var step: StepA = StepA.one
}
the compiler tells me that:
Type 'SomeComponentOfTypeA' does not conform to protocol 'Component'
Basically, it doesn't know that since I am implementing the ComponentA, my step should be of type StepA too.
As a workaround I found that if I modify this part:
protocol Component {
var step: Step { get }
}
into:
protocol Component {
associatedtype SomeStep: Step
var step: SomeStep { get }
}
Everything works just fine, a.step is of StepA, no issues at all...
Question: why is this working? I am basically hiding the fact that step property from Component is of type Step, by using the associated type SomeStep. This is kinda weird, isn't it? I was expecting this to work just fine without having to hide anything, why is this happening? How does it work?
Thanks. Any help would be much appreciated!
Protocol can be adopted by a struct or enum so it don't do class polymorphism, because they can not be inherited. This is also one of the reasons that why associatedtype
exists.
Back to your question, you defined a variable with the same name as the parent protocol in the sub-protocol, but the type is different, this is allowed because the protocol is not a concrete type. But this is not a type overload, because the protocol works differently from the class.
In this case, you'd better use generics, which is associatedtype
. But if you have to use these three protocols, you only need to declare the sub-protocol.
...
protocol ComponentA: Component {}
protocol ComponentB: Component {}
struct SomeComponentOfTypeA: ComponentA {
var step: Step = StepA.one
}
struct SomeComponentOfTypeB: ComponentB {
var step: Step = StepB.two
}
let a = SomeComponentOfTypeA()
print(a.step)
print(type(of: a))
let b = SomeComponentOfTypeB()
print(b.step)
print(type(of: b))
Output:
one
SomeComponentOfTypeA
two
SomeComponentOfTypeB
Another way is to meet your needs. Limit SomeComponentOfTypeA
only allows StepA
type.
protocol StepAProtocol { /* body */ }
protocol StepBProtocol { /* body */ }
enum StepA: Int, CaseIterable, StepAProtocol {
case one
case two
case three
}
enum StepB: Int, CaseIterable, StepBProtocol {
case one
case two
case three
}
protocol Component {
associatedtype T
var step: T { get }
}
protocol ComponentA: Component where T: StepAProtocol {}
protocol ComponentB: Component where T: StepBProtocol {}
struct SomeComponentOfTypeA: ComponentA {
var step: StepA = .one
}
struct SomeComponentOfTypeB: ComponentB {
var step: StepB = .two
}
struct SomeComponentOfTypeC: ComponentB {
var step: StepA = .two // error: Type 'StepA' does not conform to protocol 'StepBProtocol'
}