Following the discussions here on SO I already read several times the remark that mutable structs are evil (like in the answer to this question).
What's the actual problem with mutability and structs?
Following the discussions here on SO I already read several times the remark that mutable structs are evil (like in the answer to this question).
What's the actual problem with mutability and structs?
Where to start ;-p
Eric Lippert's blog is always good for a quote:
This is yet another reason why mutable value types are evil. Try to always make value types immutable.
First, you tend to lose changes quite easily... for example, getting things out of a list:
Foo foo = list[0];
foo.Name = "abc";
what did that change? Nothing useful...
The same with properties:
myObj.SomeProperty.Size = 22; // the compiler spots this one
forcing you to do:
Bar bar = myObj.SomeProperty;
bar.Size = 22;
myObj.SomeProperty = bar;
less critically, there is a size issue; mutable objects tend to have multiple properties; yet if you have a struct with two int
s, a string
, a DateTime
and a bool
, you can very quickly burn through a lot of memory. With a class, multiple callers can share a reference to the same instance (references are small).
(still thinking...)
It doesn’t have anything to do with structs (and not with C#, either) but in Java you might get problems with mutable objects when they are e.g. keys in a hash map. If you change them after adding them to a map and it changes its hash code, evil things might happen.
I wouldn't say evil but mutability is often a sign of overeagerness on the part of the programmer to provide a maximum of functionality. In reality, this is often not needed and that, in turn, makes the interface smaller, easier to use and harder to use wrong (= more robust).
One example of this is read/write and write/write conflicts in race conditions. These simply can't occur in immutable structures, since a write is not a valid operation.
Also, I claim that mutability is almost never actually needed, the programmer just thinks that it might be in the future. For example, it simply doesn't make sense to change a date. Rather, create a new date based off the old one. This is a cheap operation, so performance is not a consideration.
Structs are value types which means they are copied when they are passed around.
So if you change a copy you are changing only that copy, not the original and not any other copies which might be around.
If your struct is immutable then all automatic copies resulting from being passed by value will be the same.
If you want to change it you have to consciously do it by creating a new instance of the struct with the modified data. (not a copy)
There are many advantages and disadvantages to mutable data. The million-dollar disadvantage is aliasing. If the same value is being used in multiple places, and one of them changes it, then it will appear to have magically changed to the other places that are using it. This is related to, but not identical with, race conditions.
The million-dollar advantage is modularity, sometimes. Mutable state can allow you to hide changing information from code that doesn't need to know about it.
The Art of the Interpreter goes into these trade offs in some detail, and gives some examples.
Value types basically represents immutable concepts. Fx, it makes no sense to have a mathematical value such as an integer, vector etc. and then be able to modify it. That would be like redefining the meaning of a value. Instead of changing a value type, it makes more sense to assign another unique value. Think about the fact that value types are compared by compraing all the values of its properties. The point is that if the properties are the same then it is the same universal representation of that value.
As Konrad mentions it doesn't make sense to change a date either, as the value represents that unique point in time and not an instance of a time object which has any state or context-dependency.
Hopes this makes any sense to you. It is more about the concept you try to capture with value types than practical details, to be sure.
Imagine you have an array of 1,000,000 structs. Each struct representing an equity with stuff like bid_price, offer_price (perhaps decimals) and so on, this is created by C#/VB.
Imagine that array is created in a block of memory allocated in the unmanaged heap so that some other native code thread is able to concurrently access the array (perhaps some high-perf code doing math).
Imagine the C#/VB code is listening to a market feed of price changes, that code may have to access some element of the array (for whichever security) and then modify some price field(s).
Imagine this is being done tens or even hundreds of thousands of times per second.
Well lets face facts, in this case we really do want these structs to be mutable, they need to be because they are being shared by some other native code so creating copies isn't gonna help; they need to be because making a copy of some 120 byte struct at these rates is lunacy, especially when an update may actually impact just a byte or two.
Hugo