Swift and mutating struct

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. :)


A structure is an aggregation of fields; if a particular structure instance is mutable, its fields will be mutable; if an instance is immutable, its fields will be immutable. A structure type must thus be prepared for the possibility that the fields of any particular instance may be mutable or immutable.

In order for a structure method to mutate the fields of the underlying struct, those fields have to be mutable. If a method that mutates fields of the underlying struct is invoked upon an immutable structure, it would try to mutate immutable fields. Since nothing good could come of that, such invocation needs to be forbidden.

To achieve that, Swift divides structure methods into two categories: those that modify the underlying structure, and thus may only be invoked on mutable structure instances, and those that do not modify the underlying structure and should thus be invokable on both mutable and immutable instances. The latter usage is probably the more frequent, and is thus the default.

By comparison, .NET presently (still!) offers no means of distinguishing structure methods that modify the structure from those that don't. Instead, invoking a structure method on an immutable structure instance will cause the compiler to make a mutable copy of the structure instance, let the method do whatever it wants with it, and discard the copy when the method is done. This has the effect of forcing the compiler to waste time copying the structure whether or not the method modifies it, even though adding the copy operation will almost never turn what would be semantically-incorrect code into semantically-correct code; it will merely cause code that is semantically wrong in one way (modifying an "immutable" value) to be wrong in a different way (allowing code to think it's modifying a structure, but discarding the attempted changes). Allowing struct methods to indicate whether they will modify the underlying structure can eliminate the need for a useless copy operation, and also ensures that attempted erroneous usage will get flagged.


The mutability attribute is marked on a storage (constant or variable), not a type. You can think struct has two modes: mutable and immutable. If you assign a struct value to an immutable storage (we call it let or constant in Swift) the value becomes immutable mode, and you cannot change any state in the value. (including calling any mutating method)

If the value is assigned to a mutable storage (we call it var or variable in Swift), you're free to modify the state of them, and calling of mutating method is allowed.

In addition, classes don't have this immutable/mutable mode. IMO, this is because classes are usually used to represent reference-able entity. And reference-able entity is usually mutable because it's very hard to make and manage reference graphs of entities in immutable manner with proper performance. They may add this feature later, but not now at least.

For Objective-C programmers, mutable/immutable concepts are very familiar. In Objective-C we had two separated classes for each concept, but in Swift, you can do this with one struct. Half work.

For C/C++ programmers, this is also very familiar concept. This is exactly what const keyword do in C/C++.

Also, immutable value can be very nicely optimised. In theory, Swift compiler (or LLVM) can perform copy-elision on values passed by let, just like in C++. If you use immutable struct wisely, it will outperform refcounted classes.

Update

As @Joseph claimed this doesn't provide why, I am adding a little more.

Structs have two kind of methods. plain and mutating methods. Plain method implies immutable (or non-mutating). This separation exists only to support immutable semantics. An object in immutable mode shouldn't change its state at all.

Then, immutable methods must guarantee this semantic immutability. Which means it shouldn't change any internal value. So compiler disallows any state changes of itself in a immutable method. In contrast, mutating methods are free to modify states.

And then, you may have a question of why immutable is the default? That's because it's very hard to predict the future state of mutating values, and that usually becomes the main source of headaches and bugs. Many people agreed that the solution is avoiding mutable stuffs, and then immutable by default was on top of wish list for decades in C/C++ family languages and its derivations.

See purely functional style for more details. Anyway, we still need mutable stuffs because immutable stuffs have some weaknesses, and discussing about them seems to be out of topic.

I hope this helps.