Here is an explanation from “Swift in 60 seconds” book, by Paul Hudson:
"If a struct has a variable property but the instance of the struct was created as a constant, that property can’t be changed – the struct is constant, so all its properties are also constant regardless of how they were created.
The problem is that when you create the struct, Swift has no idea whether you will use it with constants or variables, so by default it takes the safe approach: Swift won’t let you write methods that change properties unless you specifically request it.
When you want to change a property inside a method, you need to mark it using the mutating keyword, like this:
struct Person {
var name: String
mutating func makeAnonymous() {
name = "Anonymous"
}
}
Because it changes the property, Swift will only allow that method to be called on Person instances that are variables:
var person = Person(name: “Ed”)
person.makeAnonymous()
Thanks Peter…, that is pointing me in the right direction. I am still digging into this because it still doesn’t make sense to me quite yet. In my mind, the compiler should 100% be able to know if an instance of a struct was declared with “var” or “let”, right? It can tell this in every other case I can think of because it has yelled at me many times for not using “let”
or to change to “var”, etc.
I understand why you don’t get some of the things. The way they explain the concepts in this tutorial is… unusual (politely said). If this is your first encounter with Swift, I would consider some other sources. There are better and easier ways for explaining a concept. The one presented in this course, is not the best one, nor the easiest one.
What I do is to learn from somewhere else and then come here to hear what they have to say about a given concept. In most of the cases, I get more confused… but that’s just me.
To answer your question: you declare a method as mutating, when you want to perform some changes on a property, inside the struct. At that time, the compiler does not know if you’re going to declare an instance of the struct as variable, or as constant.
But, to be able to perform the “mutation”, your instance has to be able to accept changes… that is, has to be declared as variable.
Not sure your reply could have been any better for me; that was perfect.
It is validating my experiences learning Swift.
re:Structures - It’s starting to click but for now, I just have it memorized as a rule.
Thanks @peter0123, that was extremely helpful!
Yes it can. The design of Swift, though, includes many cases where the compiler knows what to do, but instead of doing it, flags it and makes you do it. That is by design, with the idea being that you also should know, and should make the code explicit.
For instance, it knows when you need to capture self in a closure, so it could do that automatically, but instead it flags it and makes you do it - so it is clear in the code and clear that it is intentional.
So, the question from the compiler is “do you realize that constants of this struct will not be able to call this method because it makes a new copy of the struct?” If your answer is yes, you say “mutating.”
I’m sure there is something fundamental I’m missing.
Below is a struct with no constant properties but, it still gives an error if you don’t use “mutating func checkTwo(…)”
I can see why this would be the case if you used “let letChecking” as shown below but
not “var varChecking”.
The way I see it is “one” is mutable and “varChecking” is mutable.
There’s nothing here that I can see that is immutable.
struct LetsSee {
var one: String
var two: Int
func checkOne() {
print("This is \(one) and \(two)")
}
// ERROR: Cannot assign to property: 'self' is immutable.
// Mark method 'mutating' to make 'self' mutable
func checkTwo(show me: String) {
one = me
}
It doesn’t matter whether or not you have
let letChecking = …
somewhere or not. You could have it, or you could add it later.
The method named checkTwo can only be used on variable instances of LetsSee, not on constant instances of LetsSee. The compiler knows that, but it insists that you make it explicit in the code. It is not going to “take care of it” for you.
If you put in mutating, and make letChecking a constant, and try to use checkTwo on it, it will display this error message:
Cannot use mutating member on immutable value: ‘letChecking’ is a ‘let’ constant
That means that the method checkTwo is a mutating member. The compiler will insist that you identify it as such, even though it already knows it. The code must be explicit, so that there is no hidden property lurking there.
func checkTwo(…) is only operating on “var one”, which isn’t a constant, which means it is mutable, so why would you need to add the qualifier ‘mutable’ to the func name?
I think there’s something inherently interesting about Structures - they are pushed on the stack (as a local variable) vs. heap-based like Classes, but the mechanics are beyond me
If the method can only be used on variables of the struct, but not on constants, then something has to identify it as such, other wise it would be a hidden fact.
I get your poin cyberkeyboarder and I’ll try my best to explain.
You need to mark the function as “mutating” because you’re changing a property… you’re performing a mutation on a property.
When you declare a property inside a struct, that property gets a free initialiser, called member-wise initialiser. So, you can think of that property as an empty box:
var one: String = “empty box here”
Your first method ( checkOne () ), does not change anything on these properties. Therefore, it does not need the “mutating” keyword. The only thing that does, is to print out the values of the properties (or the content of the box). And these values will be the ones that you’ll assign, when you instantiate your struct → when you create an instance of the “LetsSee”.
At that time “empty box” will be replaced / filled in with the value you give.
The second method however, it does something to your property: it assigns “me” to one. So it changes, it mutates the initial value (the empty box), which your property has benefited from, with the help of member-wise initialiser!