Reputation: 70113
I have a protocol and its extension, and a class conforming to the protocol.
protocol WarAbilities {
var strength: Int { get set }
func attack(inout opponent: WarAbilities)
}
extension WarAbilities {
func attack(inout opponent: WarAbilities) {
opponent.strength -= 1
}
}
class Warrior: WarAbilities {
var strength: Int
init(strength: Int) {
self.strength = strength
}
}
Now if I want to make two warriors fight:
let thug1 = Warrior(strength: 10)
let thug2 = Warrior(strength: 30)
thug1.attack(&thug2)
I get this error message:
error: cannot pass immutable value of type 'WarAbilities' as inout argument
Adding mutating
looked promising:
protocol WarAbilities {
var strength: Int { get set }
mutating func attack(inout opponent: WarAbilities)
}
extension WarAbilities {
mutating func attack(inout opponent: WarAbilities) {
opponent.strength -= 1
}
}
But the compiler isn't happy either and I fail to understand what the new error message means:
error: cannot pass immutable value as inout argument: implicit conversion from 'Warrior' to 'WarAbilities' requires a temporary
Since Warrior
conforms to WarAbilities
I thought one of those would work - but it looks like Swift doesn't have this kind of... covariance? I'm not even sure what I'm talking about here.
What's my mistake?
Upvotes: 2
Views: 883
Reputation: 5589
Inout with protocol support:
protocol IPositional{
func setPosition(position:CGPoint)
}
extension IPositional{
var positional:IPositional {get{return self as IPositional}set{}}
}
class A:IPositional{
var position:CGPoint = CGPoint()
func setPosition(position:CGPoint){
self.position = position
}
}
func test(inout positional:IPositional){
positional.setPosition(CGPointMake(10,10))
}
var a = A()
test(&a.positional)
a.position//(10.0, 10.0)
Conclusion:
The benefit of doing it this way: Is that you can now have one "inout method" for all classes that implements IPositional
Upvotes: 0
Reputation: 57164
Two things are wrong in my opinion:
let
-constant to inout
inout
Note that in your example you do not even need (see matt's answer for proper dealing with this issue)inout
since you do not overwrite the parameter, but some instance member of that parameter. Simply dropping the inout
will probably work in your case since you do not need it at all.
Note secondly that using mutating
probably is not going to do you any good here since that only is talking about mutating its own members, not the members of the parameters.
Explanation of the problem:
Lets consider the far simpler problem which produces the exact same problem output:
protocol A {}
class B : A {}
func c(inout d : A) {}
let a = B()
c(&a)
That firstly does not work because a
is a constant. Therefore change the let
to var
.
Then the compiler complains about requiring a temporary. That is a quite confusing error message but the problem actually makes sense.
You cannot pass a subtype of some Type to an inout
or an implementation of some protocol to an inout
expecting the protocol
Explanation - consider the following example:
class A {}
class B : A {}
func c(inout a : A) { a = A() }
What should happen if you call that method via
var b = B()
c(&b)
b
would be changed and it would now be the same as a
- but a
is not of type B
which b
is said to be.
Note that the following will work again:
var b : A = B()
c(&b)
We pass in a B
but actually only worry about it being an A
. Same goes for protocols:
protocol P {}
class K : P {}
class X : P {}
func f(inout p : P) { p = X() }
var k = K()
f(&k) // does not work
var p : P = K()
f(&p) // works
Upvotes: 2
Reputation: 535606
Make it a class
protocol and get rid of the (then) unnecessary inout
stuff:
protocol WarAbilities : class {
var strength: Int { get set }
func attack(opponent: WarAbilities)
}
extension WarAbilities {
func attack(opponent: WarAbilities) {
opponent.strength -= 1
}
}
class Warrior: WarAbilities {
var strength: Int
init(strength: Int) {
self.strength = strength
}
}
let thug1 = Warrior(strength: 10)
let thug2 = Warrior(strength: 30)
thug1.attack(thug2)
thug2.strength // 29
(Indeed it's rather unclear to me why you need a protocol here at all; since Warrior is a class, you can just make WarAbilities its superclass.)
Upvotes: 4