Reputation: 518
I am trying to leverage protocols and extensions in Swift, but am getting a compile error I can't make sense of.
If I declare two protocols that define a Shape like this:
protocol Shape {
var sides : Int { get }
var fill : Fill { get }
}
protocol Fill {
var color : UIColor { get }
}
Now to implement this, I define two structs one for a Square, and the other for a solid fill. Like this:
struct SolidFill : Fill {
var color : UIColor
}
struct Square : Shape {
var sides : Int = 4
var fill : SolidFill = SolidFill(color: UIColor.blackColor())
}
I receive a compile error "Type 'Square' does not conform to protocol 'Shape'". If I force the type of fill to be fill like var fill : Fill
the compile error goes away. Why can't I have Square specify a more specific type for fill, then what the protocol allows?
I realize that for this particular example, I could change things to not use this pattern by using an Enum for fill or reworking something else. I just want to know why I can't have fill be a type that conforms to the protocol.
Upvotes: 0
Views: 82
Reputation: 62062
Before you start writing code that conforms to protocols, you need to think about your protocols and realize that in a normal use case (and the way that your protocols should be designed), code will be written only with knowledge of the protocol and with zero knowledge of any specific class or struct which implements the protocol.
So, it does make sense for the Fill
protocol to have other protocols that inherit from it, such as SolidFill
and perhaps StripedFill
.
Let's go ahead and add the StripedFill
protocol to your example. Now let's look at your Square
struct and see why it doesn't implement the Shape
protocol, which in part requires having a Fill
property.
struct Square : Shape {
var sides : Int = 4
var fill : SolidFill = SolidFill(color: UIColor.blackColor())
}
The only thing we can assign to Square
's fill
property are things which implement the SolidFill
protocol. But the Shape
protocol requires that our shape be able to assign to a property called fill
anything which conforms to the Fill
protocol. And in the case where we also have a protocol called StripedProtocol
which inherits from the Fill
protocol, that would include objects that implement that protocol (whether or not they also implement the SolidFill
protocol).
But your Square
class doesn't allow for this. Your Square
class only allows for one specific child of Fill
and its descendants, but not its sibling... and the exclusion of SolidFill
's potential siblings is why you can't do what you're trying to do.
What you can do however is this:
struct Square: Shape {
var sides: Int = 4
var fill: Fill = SolidFill(color: UIColor.blackColor())
}
So in this case, we're still definitely assigning a SolidFill
to our Square
, but we're still allowing for the fill
property to match what it defined in the protocol and allowing for SolidFill
's siblings to be assigned to the fill
property.
Upvotes: 1
Reputation: 6504
With only a getter it may be theoretically OK but if there was a setter it would definitely not fulfil the protocol as someone may try to set a different object that fulfils the protocol.
If you think it is sufficiently valuable you could raise a radar requesting it for get only vars but you are probably better working around it.
You could do this:
struct Square : Shape {
var sides : Int = 4
var fill : Fill { solidFill }
private var solidFill = SolidFill(color: UIColor.blackColor())
}
Upvotes: 0