Caution: layman's terms ahead.
This explanation isn't rigorously correct at the most nitty-gritty code level. However it has been reviewed by a guy who actually works on Swift and he said it's good enough as a basic explanation.
So I want to try to simply and directly answer the question of "why".
To be precise: why do we have to mark struct functions as mutating
when we can change struct parameters without any modifying keywords?
So, big picture, it has a lot to do with the philosophy that keeps Swift swift.
You could kind of think of it like the problem of managing actual physical addresses. When you change your address, if there are a lot of people who have your current one, you have to notify all of them that you've moved. But if no one has your current address, you can just move wherever you want, and no one needs to know.
In this situation, Swift is kind of like the post office. If lots of people with lots of contacts move around a lot, it has a really high overhead. It has to pay a big staff of people to handle all those notifications, and the process takes up a lot of time and effort. That's why Swift's ideal state is for everyone in its town to have as few contacts as possible. Then it doesn't need a big staff for handling address changes, and it can do everything else faster and better.
This is also why Swift-folks are all raving on about value types vs. reference types. By nature, reference types rack up "contacts" all over the place, and value types usually don't need more than a couple. Value types are "Swift"-er.
So back to small picture: structs
. Structs are a big deal in Swift because they can do most of the things objects can do, but they're value types.
Let's continue the physical address analogy by imagining a misterStruct
that lives in someObjectVille
. The analogy gets a little wonked up here, but I think it still is helpful.
So to model changing a variable on a struct
, let's say misterStruct
has green hair, and gets an order to switch to blue hair. The analogy gets wonked, like I said, but sort of what happens is that instead of changing misterStruct
's hair, the old person moves out and a new person with blue hair moves in, and that new person begins calling themselves misterStruct
. No one needs to get a change of address notification, but if anyone looks at that address, they'll see a guy with blue hair.
Now let's model what happens when you call a function on a struct
. In this case, it's like misterStruct
gets an order such as changeYourHairBlue()
. So the post office delivers the instruction to misterStruct
"go change your hair to blue and tell me when you're done."
If he's following the same routine as before, if he's doing what he did when the variable was changed directly, what misterStruct
will do is move out of his own house and call in a new person with blue hair. But that's the problem.
The order was "go change your hair to blue and tell me when you're done," but it's the green guy who got that order. After the blue guy moves in, a "job complete" notification still has to be sent back. But the blue guy knows nothing about it.
[To really wonk up this analogy something awful, what technically happened to green-haired guy was that after he moved out, he immediately committed suicide. So he can't notify anyone that the task is complete either!]
To avoid this problem, in cases like this only, Swift has to go in directly to the house at that address and actually change the current inhabitant's hair. That is a completely different process than just sending in a new guy.
And that's why Swift wants us to use the mutating
keyword!
The end result looks the same to anything that has to refer to the struct: the inhabitant of the house now has blue hair. But the processes for achieving it are actually completely different. It looks like it's doing the same thing, but it's doing a very different thing. It's doing a thing that Swift structs in general never do.
So to give the poor compiler a little help, and not make it have to figure out whether a function mutates the struct
or not, on its own, for every single struct function ever, we are asked to have pity and use the mutating
keyword.
In essence, to help Swift stay swift, we all must do our part. :)