Swift and mutating struct

StructValue TypeSwift

Struct Problem Overview


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?

Struct Solutions


Solution 1 - Struct

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.

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

Solution 3 - Struct

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.

Solution 4 - Struct

Swift structs can be instantiated as either constants (via let) or variables (via var)

Consider Swift's Array struct (yes it's a struct).

var petNames: [String] = ["Ruff", "Garfield", "Nemo"]
petNames.append("Harvey") // ["Ruff", "Garfield", "Nemo", "Harvey"]

let planetNames: [String] = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
planetNames.append("Pluto") //Error, sorry Pluto. No can do

Why didn't the append work with the planet names? Because append is marked with the mutating keyword. And since planetNames was declared using let, all methods thus marked are off limits.

In your example the compiler can tell you're modifying the struct by assigning to one or more of its properties outside of an init. If you change your code a bit you'll see that x and y aren't always accessible outside the struct. Notice the let on the first line.

let p = Point(x: 1, y: 2)
p.x = 3 //error
p.moveToX(5, andY: 5) //error

Solution 5 - Struct

Consider an analogy with C++. A struct method in Swift being mutating/not-mutating is analogous to a method in C++ being non-const/const. A method marked const in C++ similarly cannot mutate the struct.

> You can change a var from outside a struct, but you cannot change it > from its own methods.

In C++, you can also "change a var from outside the struct" -- but only if you have a non-const struct variable. If you have a const struct variable, you cannot assign to a var, and you also cannot call a non-const method. Similarly, in Swift, you can change a property of a struct only if the struct variable is not a constant. If you have a struct constant, you cannot assign to a property, and you also cannot call a mutating method.

Solution 6 - Struct

I wondered the same thing when I started learning Swift, and each of these answers, while adding some insights perhaps, are kind of wordy and confusing in their own right. I think the answer to your question is actually pretty simple…

The mutating method defined inside your struct wants permission to modify each and every instance of itself that will ever be created in the future. What if one of those instances gets assigned to an immutable constant with let? Uh oh. To protect you from yourself (and to let the editor and compiler know what you're trying to do), you're forced to be explicit when you want to give an instance method this sort of power.

By contrast, the setting of a property from outside your struct is operating on a known instance of that struct. If it's assigned to a constant, Xcode will let you know about it the moment you've typed in the method call.

This is one of the things I'm loving about Swift as I start to use it more—being alerted to errors as I type them. Sure beats the heck out of troubleshooting obscure JavaScript bugs!

Solution 7 - Struct

SWIFT : Use of mutating function in Structs

Swift programmers developed Structs in such a way that properties of it can not be modified within Struct methods. For example, Check the code given below

struct City
{
  var population : Int 
  func changePopulation(newpopulation : Int)
  {
      population = newpopulation //error: cannot modify property "popultion"
  }
}
  var mycity = City(population : 1000)
  mycity.changePopulation(newpopulation : 2000)

On executing the above code we get an error because we are trying to assign a new value to the property population of the Struct City. By default Structs properties can’t be mutated inside its own methods. This is how Apple Developers have built it, so that the Structs will have a static nature by default.

How do we solve it? What’s the alternative?

Mutating Keyword :

Declaring function as mutating inside Struct allows us to alter properties in Structs. Line No : 5, of the above code changes to like this,

mutating changePopulation(newpopulation : Int)

Now we can assign the value of the newpopulation to property population to within the scope of the method.

Note :

let mycity = City(1000)     
mycity.changePopulation(newpopulation : 2000)   //error: cannot modify property "popultion"

If we use let instead of var for Struct objects, then we can’t mutate the value of any properties, Also this is the reason why we get an error when we try to invoke mutating function using let instance. So it is better to use var whenever you are changing value of a property.

Would love to hear your comments and thoughts…..

Solution 8 - Struct

One more variant

struct MyStruct {
    var myVar = "myVar"
    let myLet = "myLet"
}

func testMutateString() {
    //given
    let myStruct = MyStruct()
    
    //Change var
    //when
    var myStructCopy = myStruct
    myStructCopy.myVar = "myVar changed 1"
    
    //then
    XCTAssert(myStructCopy.myVar == "myVar changed 1")
    
    //Change let
    //when
    withUnsafeMutableBytes(of: &myStructCopy) { bytes in
        let offset = MemoryLayout.offset(of: \MyStruct.myLet)!
        let rawPointerToValue = bytes.baseAddress! + offset
        let pointerToValue = rawPointerToValue.assumingMemoryBound(to: String.self)
        pointerToValue.pointee = "myLet changed"
    }
    
    //then
    XCTAssert(myStructCopy.myLet == "myLet changed")
}

[Swift types]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMike SeghersView Question on Stackoverflow
Solution 1 - StructeonilView Answer on Stackoverflow
Solution 2 - StructLe Mot JuicedView Answer on Stackoverflow
Solution 3 - StructsupercatView Answer on Stackoverflow
Solution 4 - StructLanceView Answer on Stackoverflow
Solution 5 - StructnewacctView Answer on Stackoverflow
Solution 6 - StructKalView Answer on Stackoverflow
Solution 7 - StructsDevView Answer on Stackoverflow
Solution 8 - StructyoAlex5View Answer on Stackoverflow