Search code examples
structvalue-typeswift

Swift and mutating struct


There is something that I don't entirely understand when it comes to mutating value types in Swift.

As the "The Swift Programming Language" iBook states: By default, the properties of a value type cannot be modified from within its instance methods.

And so to make this possible we can declare methods with the mutating keyword inside structs and enums.

The thing that is not entirely clear to me is this: You can change a var from outside a struct, but you cannot change it from its own methods. This seems counter-intuitive to me, as in Object Oriented languages, you generally try to encapsulate variables so they can only be changed from within. With structs this appears to be the other way around. To elaborate, here's a code snippet:

struct Point {
    var x = 0, y = 0
    mutating func moveToX(x: Int, andY y:Int) { //Needs to be a mutating method in order to work
        self.x = x
        self.y = y
    }
}

var p = Point(x: 1, y: 2)
p.x = 3 //Works from outside the struct!
p.moveToX(5, andY: 5) 

Does anyone know the reason why structs cannot change their content from inside their own context, while the contents can easily be changed elsewhere?


Solution

  • 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.