views:

2180

answers:

7

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?

+25  A: 

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 ints, 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...)

Marc Gravell
Well yes but the compiler is just stupid that way. Not to allow assignment to property-struct members was IMHO a stupid design decision, because it *is* allowed for `++` operator. In this case, the compiler just writes the explicit assignment itself instead of hustling the programmer.
Konrad Rudolph
@Konrad: myObj.SomeProperty.Size = 22 would modify a COPY of myObj.SomeProperty. The compiler is saving you from an obvious bug. And it is NOT allowed for ++.
Lucas
@Lucas: Well, the Mono C# compiler certainly allows it – since I don’t have Windows I can’t check for Microsoft’s compiler.
Konrad Rudolph
@Konrad - MS compiler says no to: ` class Test { public MyStruct Value { get; set; } public struct MyStruct { public int Bar { get; set; } } public static void Main() { Test test = new Test(); test.Value.Bar++; } }` - does that work on mono?
Marc Gravell
@Marc: I was actually talking about one less indirection: `test.Bar++` or `test.Bar += 1`. This is also mutating the value – in fact, it’s more or less equivalent to `var tmp = test.Bar; tmp += 1; test.Bar = tmp;`. So the situation is not completely identical but that was never my point. My point was that the compiler can and does rewrite code where it makes sense. And the resulting rewrite here is almost completely identical to the manual rewrite you proposed in your answer. The only thing that’s different is `bar.Size = 22;` vs. `bar = bar + 1`.
Konrad Rudolph
@Marc: Duh. I take that back – there’s a crucial difference here, my code is *always* creating a new object, it also works with immutables.
Konrad Rudolph
@Konrad - with one less indirection it should work; it is the "mutating a value of something that only exists as a transient value on the stack and which is about to evaporate into nothingness" which is the case that is blocked.
Marc Gravell
@Marc Gravell: In the former piece of code, you end up with a "Foo" whose name is "abc" and whose other attributes are those of List[0], without disturbing List[0]. If Foo were a class, it would be necessary to clone it and then change the copy. To my mind, the big problem with the value-type vs class distinction is the use of the "." operator for two purposes. If I had my druthers, classes could support both "." and "->" for methods and properties, but the normal semantics for "." properties would be to create a new instance with the appropriate field modified.
supercat
+3  A: 

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.

Bombe
The same is true in C#, so good point, yes.
Konrad Rudolph
That is true if you use a class as the key in a map, too.
Marc Gravell
+11  A: 

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.

Konrad Rudolph
Eric Lippert says they are... see my answer.
Marc Gravell
Immutability is certainly good with threading, but you can write an immutable class just as easily and just as usefully. But still a good answer. I would +1, but I'm out for today.
Marc Gravell
Much as I respect Eric Lippert he isn't God (or at least not yet). The blog post you link to and your post above are reasonable arguments for making structs immutable as matter of course but they are actually very weak as arguments for **never** using mutable structs. This post, however, is a +1.
Stephen Martin
+15  A: 

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)

trampster
"If your struct is immutable then all copies will be the same." No, it means that you have to consciously make a copy if you want a different value. It means you won't get caught modifying a copy thinking you are modifying the original.
Lucas
@Lucas I think you are talking about a different kind of copy I am talking about the automatic copies made as a result of being passed by value, Your 'consciously made copy' is different on purpose you didn't make it by mistake and its not really a copy its a deliberate new instants containing different data.
trampster
@trampster Your edit (16 months later) makes that a little clearer. I still stand by "(immutable struct) means you won't get caught modifying a copy thinking you are modifying the original", though.
Lucas
+3  A: 

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.

Glomek
structs are not be aliased in c#. Every struct assignment is a copy.
recursive
@recursive: In some cases, that's a major advantage of mutable structs, and one which makes me question the notion that structs should not be mutable. The fact that compilers sometimes implicitly copy structs doesn't reduce the usefulness of mutable structs.
supercat
+6  A: 

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.

Morten Christiansen
Well, they **should** represent immutable concepts, at least ;-p
Marc Gravell
True, but I suppose you can misuse most programming constructs
Morten Christiansen
Well, I suppose they could have made System.Drawing.Point immutable but it would have been a serious design error IMHO. I think points are actually an archetypical value type and they are mutable. And they don't cause any problems for anyone beyond really early programming 101 beginners.
Stephen Martin
In principle I think points should also be immutable but if it makes the type harder or less elegant to use then of course that has to be considered too. There's no point in having code constructs which uphold the finest princicples if no one wants to use them ;)
Morten Christiansen
A: 

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

Hugo
True, but in this case the reason for using a struct is that doing so is imposed upon the application design by outside constraints (those by the native code's use). Everything else you describe about these objects suggests they should clearly be classes in C# or VB.NET.
Jon Hanna