Best way to modify on Cannot use mutating member on immutable value: self is immutable


#1

Sort of newbie to Swift... Appreciate if any sme can point me in the right direction.

I am modifying an existing code base ; in that I have one swift file as below.
The highlighted bold and italic line has the error
return try? generator.p(at: currentTime)
"Cannot use mutating member on immutable value: 'self' is immutable"

The generator (another swift class) is made mutating as I have to assign a computed value to its member.

I read structs are immutable in swift. What can I do to make the change local to this file and yet don't break things in the rest of the package?

Many thanks in advance.

import Foundation
public struct Token: MyToken {
public var name: String
public var generator: Generator

public init(name: String = "",  generator: Generator) {
    self.name = name
      self.generator = generator
}

public var currentP: String? {
    let currentTime = Date()
    **_return try? generator.p(at: currentTime)_**
}

public func updatedToken() -> Token {
    return Token(name: name,  generator: generator.successor())
}

}


#2

One option is to mark currentP as mutating.

Another is to make Token a class.

A third is to make Generator a class.

The decision regarding which approach makes the most sense, is something you’ll have to make based on the specifics of the code itself.


(Jeremy David Giesbrecht) #3

That may be impossible. [Edit: See @benrimmington’s post.]

@Nevin has done a good job of listing all your options, but you should know that all of them will require you to audit all the code that uses your Token type, to make sure your changes didn’t break anything.

That is probably what I would do, though you will also need to convert it to a method to apply mutating: [Edit: See @benrimmington’s post.]

public mutating func currentP() -> String? {
    // ...
}

The side effect is that every caller has to add parentheses. This is likely the most convenient solution, because the compiler will immediately catch most uses for you. But there are still some that may sneak by:

// This will still compile, but will no longer describe the optional string.
print(String(describing: x.currentP))
/// Instead it will describe the function itself.

If you switch either one from a struct to a class, it will immediately go from value to reference semantics. The compiler may flag a few things for you: instances of mutating that are now invalid, instances of var that should be changed to let because there is no longer any mutation happening, etc. But what you really need to watch out for is chronic logic problems which pop up:

class SomeType {
    var token: Token
    func modifiedToken() -> Token {
        // This will still compile,
        // but it will now be causing permanent side‐effects to `self`,
        // since `localCopy` is not really a distinct copy anymore.
        var localCopy = self.token
        localCopy.changeSomething()
        if localCopy.hasSomethingToFilterOut {
            localCopy = someReplacementToken()
        }
        return localCopy
    }
}

No matter what you decide to do, exhaustive tests will be your best friend. The better the test coverage of the code base before you started, the safer you can feel about the changes you make.


(Ben Rimmington) #4

You should be able to add mutating to the getter of the currentP property.

   public var currentP: String? {
+    mutating get {
       let currentTime = Date()
       return try? generator.p(at: currentTime)
+    }
   }

(Jeremy David Giesbrecht) #5

Cool. Thanks. I didn’t know that was possible. It would have come in helpful many times.


#6

Thanks to all! I went with Ben's suggestion.

If I may ask a followup guidance .. appreciate if I can get any answer on these

===
init (persistentToken: PersistentToken, displayTime: DisplayTime ) {
var rawP = ( try ? persistentToken.token.generator.p(at: displayTime.date)) ?? ""

and

func updatePersistentToken( _ persistentToken: PersistentToken) throws{
var newToken = persistentToken.token.updatedToken()

both code snippets give me the error: Cannot use mutating member on immutable value: 'persistentToken' is a 'let' constant

Any ideas that I can borrow? These are the only two places I get the error for the rest of the code base, so deeply appreciative of any ways to modify this.

Thanks in advance.


(Jeremy David Giesbrecht) #7
init (persistentToken: PersistentToken, displayTime: DisplayTime ) {
/* Add this line: */ var persistentToken = persistentToken
var rawP = ( try ? persistentToken.token.generator.p(at: displayTime.date)) ?? ""

That will shadow the original immutable persistantToken with a mutable copy.

Note that due to the copy, the mutation will not affect the original token. It is not clear to me if that is what you intend for your new behaviour or not. At least it should not break anything from before you started changing things, since it was originally immutable anyway.


(Brent Royal-Gordon) #8

Keep in mind that it's pretty surprising for a struct getter to be mutating. People usually expect accessing a property to have no side effects; a mutating function will be easier to understand.


#9

Thanks to all! that 'am able to complete the changes smoothly. Appreciate the guidance and very insightful ideas :) Thanks!