我可以在协议扩展中使用`inout`吗?

时间:2016-05-27 14:39:19

标签: swift

我有一个协议及其扩展,以及一个符合协议的类。

for /f "skip=3 delims=\" %%A in ('net view ^| findstr /v /C:"The command completed successfully"') do Echo %%A

现在如果我想让两个战士战斗:

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
    }
}

我收到此错误消息:

  

错误:无法将“WarAbilities”类型的不可变值作为inout参数

传递

添加let thug1 = Warrior(strength: 10) let thug2 = Warrior(strength: 30) thug1.attack(&thug2) 看起来很有希望:

mutating

但是编译器也不开心,我无法理解新错误消息的含义:

  

错误:无法将不可变值作为inout参数传递:从“Warrior”到“WarAbilities”的隐式转换需要暂时的

由于protocol WarAbilities { var strength: Int { get set } mutating func attack(inout opponent: WarAbilities) } extension WarAbilities { mutating func attack(inout opponent: WarAbilities) { opponent.strength -= 1 } } 符合Warrior我认为其中一个可行 - 但看起来Swift没有这种...协方差?我甚至不确定我在这里谈论的是什么。

我的错误是什么?

3 个答案:

答案 0 :(得分:3)

使其成为class协议并摆脱(然后)不必要的inout内容:

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

(事实上我不清楚为什么你需要一个协议;因为Warrior是一个类,你可以让WarAbilities成为它的超类。)

答案 1 :(得分:2)

我认为有两件事是错的:

  1. let - 常量传递给inout
  2. 将子类型传递为inout
  3. 请注意,在您的示例中,您甚至不需要 inout,因为您没有覆盖参数,而是该参数的某些实例成员。简单地删除inout可能会对你的情况起作用,因为根本不需要它。(请参阅matt的正确处理此问题的答案)

    其次请注意,使用mutating可能对你没有任何好处,因为那只是讨论变异自己的成员,而不是参数的成员。

    问题解释:
    让我们考虑一个产生完全相同问题输出的更简单的问题:

    protocol A {}
    class B : A {}
    
    func c(inout d : A) {}
    
    let a = B()
    c(&a)
    

    首先不起作用,因为a是一个常数。因此,请将let更改为var 然后编译器抱怨要求临时。这是一个非常令人困惑的错误消息,但问题实际上是有道理的。

    您无法将某些类型的子类型传递给inout ,或将某些协议的实现传递给期望协议的inout

    说明 - 请考虑以下示例:

    class A {}
    class B : A {}
    
    func c(inout a : A) { a = A() }
    

    如果您通过

    调用该方法会发生什么
    var b = B()
    c(&b)
    

    b会被更改,现在与a相同 - 但a不属于Bb据说是var b : A = B() c(&b)

    请注意,以下内容将再次起作用:

    B

    我们传递A,但实际上只担心它是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 。协议也是如此:

    {{1}}

答案 2 :(得分:0)

支持协议支持:

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)

<强>结论:
这样做的好处是:你现在可以拥有一个&#34; inout方法&#34;对于实现IPositional

的所有类