Since the first time I saw it I've been baffled by the need for the 'mutating' keyword. It very simple to understand the rule; but to me that's not the issue. The issue for me has always been why the keyword necessary at all. Especially since its not necessary for a class method that changes its own properties.
It seems to me that what it is really "saying" is not just that the method mutates one or more properties, but also that any variable having a mutating method invoked upon it must be declared with the var keyword, and not the let keyword. This is because (of course) a struct is a value type, and by declaring it with let you are stating that the object (variable) itself is immutable. Which is not the case for a reference type (a class).
Is it that simple? My concern up to this point had been that I thought perhaps the requirement for the keyword was meant to discourage its use. But now it seems that was probably just my interpretation because I didn't understand why it was required.
Hope everything I have said is valid and true, and may even help someone else.
Another reason is that it means it is modifying the whole object itself, not just one of its properties. This is because self is some kind of value type (like a struct) and modifying it basically means allocating/creating a copy of self + your changes.
So in some ways it may discourage its use. It can be expensive/slow to have a lot of mutations to an object and that may convince you to change to a reference type (like a class) instead. Although there are multiple ways to achieve the performance/efficiency that you need and these are just some of the options.
At a low level, is that really what happens? If I have an Int property and I mutate it, are you saying it creates a whole new struct object, just with a different value for the property? I find that hard to believe...
What are the requirements for the swift optimizer being able to optimize value-type mutations to be non-copying? The Ownership Manifesto mentions the moveonly declaration and Copyable protocol, but I don't fully understand everything in that document.
I'm assuming a fixed layout is required, which should be guaranteed naturally by using a swift struct. Then does it matter if you're modifying a reference vs value type? Are there other criteria that must be met?
If by fixed layout you mean that the order of the fields is never going to change while the program is running, then all Swift structs are fixed layout. They are not, however, guaranteed to be the same every time you compile the code unless you actually make them @_fixed_layout (which is use at your own risk at the moment).
I think the most important thing is that you make sure that you never access the original data instead of your copy in a mutating method or function with an inout argument.
struct Foo {
var bar: Int
var baz: Int
}
var qux = Foo(bar: 42, baz: 69105)
func doStuff(_ foo: inout Foo) {
foo.bar = 10
// Don't do this!
print(qux.bar)
}
doStuff(&qux)
But that's why they added the memory exclusivity rule: just to make sure this can't happen.
Oddly, the 4.1 REPL prints 10 when (assuming this is even still allowed) it should be printing 42.
EDIT: I just tried compiling it, and it crashes due to an exclusivity violation. So, yeah, this is just REPL weirdness.
are you sure this is true? i hear this repeated a lot and it just doen’t make sense that class mutation would be intrinsically more efficient than struct mutation. i think the exclusivity rule is just a safety measure that comes from the write-back semantic model
I thought that’s how it was, partly because I’d seen it stated on blog posts/tutorials about deciding between structs/classes, but it appears I was mistaken and that the exclusivity rule is intended to prevent the behavior that I previously believed was commonplace.
Yes, I previously believed that mutating any value type (like a struct) would result in a new object being created to replace the old one (exensive/slow if you’re mutating a struct a lot). It has been proven to me that this is not the case, and that mutating a value type just does the mutation in-place.
I would just remove my earlier remark, but will leave it for historical reasons so anyone else who comes across this thread can see the discussion my misguided remark sparked.