Search code examples
protocolsswift2ios9swift-extensionsswift-protocols

Swift 2 Error using mutating function in Protocol extension "Cannot use mutating member on immutable value: 'self' is immutable


Not sure what's going on here, this seems like it should be pretty straight forward. I have a protocol that mutable var, an extension with a mutating function. Things are crapping out in the testClass.testFunc, when I try and use mtkAnimQueAppend declared in the extension, I get this error: "Cannot use mutating member on immutable value: 'self' is immutable.

protocol MTKAnimateValueDelegate {
    var mtkAnimQue:[MTKAnimateValue]? {get set}
}

extension MTKAnimateValueDelegate {
    ///Adds element to que
    mutating func mtkAnimQueAppend(element:MTKAnimateValue) {

        if mtkAnimQue != nil {
          mtkAnimQue?.append(element)
        } else {
          mtkAnimQue = [element]
        }
    }
}

class testClass: MTKAnimateValueDelegate {

  var mtkAnimQue:[MTKAnimateValue]?

  func testFunc() {
    var animValue = MTKAnimateValue(fromValue: 10, toValue: 20, inSeconds: 2)
    animValue.isAnimating = true
    mtkAnimQueAppend(animValue) //ERROR: "Cannot use mutating member on immutable value: 'self' is immutable
  }

}

Solution

  • The problem is that, in the protocol you mark the function as mutating, which you need to do if you want to use the protocol on a struct. However, the self that is passed to testFunc is immutable (it's a reference to a instance of the class) and that is tripping up the compiler. This would make sense if testClass was actually a struct and you could make the function mutating to resolve the issue.

    I can see two work arounds:

    1. make the protocol class only

      protocol MTKAnimateValueDelegate: class { ...
      
    2. Make testClass a struct and mark testFunc as mutating.

    Either way, I think this is a bug that needs to be reported to Apple.

    Edit

    1. Another way around it is to make a mutable copy of self
    func testFunc() {
        var animValue = MTKAnimateValue(fromValue: 10, toValue: 20, inSeconds: 2)
        animValue.isAnimating = true
        var mutableSelf = self
        mutableSelf.mtkAnimQueAppend(animValue) 
      }
    

    Since mutableSelf is a reference, any changes the mutating function makes will still be reflected in self's state.