Reputation: 20437
I would like my protocol to declare that there is a read/write property available. I have attempted it, but this does not work:
protocol EdibleThing {
var eaten: Bool { get set }
}
class Pickle: EdibleThing { var eaten = false }
class RusticGrapefruit: EdibleThing { var eaten = false }
class Jar {
let contents: [EdibleThing] = [Pickle(), RusticGrapefruit()]
var nextItem: EdibleThing {
return contents.last ?? Pickle() // Lazy pickle generation technology
}
func eat() {
let food = nextItem
food.eaten = true // (!) ERROR: Cannot assign to result of this expression
}
}
What am I doing wrong? I think I've declared that the protocol has a get/set var called eaten
, so why can't I set it?
Upvotes: 0
Views: 1415
Reputation: 72770
The protocol might be implemented by either classes and structs - that prevents you from changing the internal status of an instance of a class or struct implementing that protocol using an immutable variable.
To fix the problem you have to either declare the food
variable as mutable:
func eat() {
var food = nextItem
food.eaten = true // (!) ERROR: Cannot assign to result of this expression
}
or declare the EdibleThing
protocol to be implementable by classes only:
protocol EdibleThing : class {
var eaten: Bool { get set }
}
Note that this happens because food
is a variable of EdibleThing
type - the compiler doesn't know if the actual instance is a value or reference type, so it raises an error. If you make it a variable of a class type, like this:
let food: Pickle = nextItem as! Pickle
the compiler knows without any ambiguity that it's a reference type, and in that case it allows the assignment. But I guess that breaks your app logic... so consider it just as an example
Upvotes: 2
Reputation: 20437
The problem is that you can't mutate a property on a value type defined by let
.
Even though both of RusticGrapefruit
and Pickle
are class implementations (reference types), the protocol could be assigned to a value type like a struct. The compiler detects a potential problem and stops us.
Two solutions:
let
to var
(in my case, this would mean changing a lot of code that refers to objects of this type. Also, I like the semantic value and possible compiler optimizations from let
)protocol EdibleThing: class { }
Upvotes: 1
Reputation: 10096
You're mutating food
.
Replace let food = nextItem
with var food = nextItem
Upvotes: 1