29

Possible Duplicate:
Why are mutable structs evil?

I read it in lots of places including here that it's better to make structs as immutable.

What's the reason behind this? I see lots of Microsoft-created structs that are mutable, like the ones in xna. Probably there are many more in the BCL.

What are the pros and cons of not following this guideline?

Community
  • 1
  • 1
Joan Venge
  • 315,713
  • 212
  • 479
  • 689

9 Answers9

44

Structs should represent values. Values do not change. The number 12 is eternal.

However, consider:

Foo foo = new Foo(); // a mutable struct
foo.Bar = 27;
Foo foo2 = foo;
foo2.Bar = 55;

Now foo.Bar and foo2.Bar is different, which is often unexpected. Especially in the scenarios like properties (fortunately the compiler detect this). But also collections etc; how do you ever mutate them sensibly?

Data loss is far too easy with mutable structs.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks Marc. Speed wise, do you think changing the value of a value in a struct is faster than creating a new struct? – Joan Venge Mar 03 '09 at 22:50
  • 2
    I doubt you'd ever notice the difference in a standard app. There may be some *very specialized* cases where you do notice, but *for general guidance*, immutable wins hands down. – Marc Gravell Mar 03 '09 at 22:58
  • 8
    Regardless of the answer (which I don't know), you should never let performance concerns trump good design _unless_ there's a bottleneck and a profiler tells you that this is the source of the problem. – Michael Meadows Mar 03 '09 at 22:59
  • Thanks Marc, I should follow this as a guideline then. – Joan Venge Mar 03 '09 at 22:59
  • 1
    Thanks Michael, the reason I was talking about speed is because to me, changing the values on a struct looks sensible, maybe because of the other languages I used or that it looks like classes. – Joan Venge Mar 03 '09 at 23:00
  • 5
    @Henk = that is different. I didn't **reassign* foo2; I updated it. For example, I don't say i.Sign = !i.Sign to *update* i; I reassign it with i = -i; ... reassigning it very different to updating properties. – Marc Gravell Mar 03 '09 at 23:15
  • My later post (hope you liked it) about how to break "readonly" C# compiler's *immutability rule*: http://stackoverflow.com/questions/4720475/why-can-struct-change-their-own-fields/4720966#4720966 – Alan Turing Oct 06 '11 at 01:46
  • 1
    I don't really see how mutable structs are bad, I don't expect to modify the original if I modify a copy, that's what I use classes for. I agree with @Henk Holterman's assessment, and I don't understand why you would want a struct to be immutable in the first place. – Jackson Tarisa Sep 01 '18 at 15:34
17

The big con is that things don't behave how you expect them to - particularly if the mutability comes from a mixture of the direct value and a reference type within it.

To be honest, I can't remember off the top of my head all the weird problems I've seen people come up with in newsgroups when they've used mutable structs - but those reasons certainly exist. Mutable structs cause problems. Stay away.

EDIT: I've just found an email I wrote a while ago on this topic. It elaborates just a little bit:

  • It's philosophically wrong: a struct should represent some sort of fundamental value. Those are basically immutable. You don't get to change the number 5. You can change a variable's value from 5 to 6, but you don't logically make a change to the value itself.

  • It's practically a problem: it creates lots of weird situations. It's particularly bad if it's mutable via an interface. Then you can start changing boxed values. Ick. I've seen a lot of newsgroup posts which are due to people trying to use mutable structs and running into issues. I saw a very strange LINQ example which was failing because List<T>.Enumerator is a struct, for example.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks Jon. Speed wise, do you think changing the value of a value in a struct is faster than creating a new struct? – Joan Venge Mar 03 '09 at 22:52
  • @Joan Venge: I'll answer that: No. Or rather, there is a method call for the constructor, but it's most likely inlined unless your constructor is particularly heavy. – Randolpho Mar 03 '09 at 22:55
  • @Randolpho: Thanks. But say for a case like .X = 2, vs X=x, Y=y, Z=z in the constructor, would it be valid? – Joan Venge Mar 03 '09 at 22:58
  • @Joan: In a very few cases it may be significantly faster. I wouldn't use a mutable struct for the sake of speed without a *massively* good reason though. – Jon Skeet Mar 03 '09 at 23:01
  • Yes, changing the value of a value in a struct is faster than creating a new struct. But unless you are performance sensitive, like XNA is, the difference won't matter. – Jonathan Allen Mar 03 '09 at 23:02
  • Thanks Jon. You are right, I am not really favoring speed over design but when you see lots of examples out there doing this, like the xna framework, etc where my development domain is, I just assume it must be better, faster, etc. – Joan Venge Mar 03 '09 at 23:04
  • Thanks Grauenwolf. I see your point, but I am also very careful with speed because writing a very large 3d based app which is very computation intensive, so trying to make things right in design earlier, rather than later. Also I don't favor bad practices, but using mutable structs don't seem... – Joan Venge Mar 03 '09 at 23:06
  • or look very unnatural to me, syntax wise, but I can understand some of the problems you all mentioned. – Joan Venge Mar 03 '09 at 23:06
  • I can see XNA being an exception (and there are already mutable structs there, I believe) - but be careful, and don't say we didn't warn you ;) – Jon Skeet Mar 03 '09 at 23:12
  • John, how is a struct variable different from an int variable? I don't dispute the advice but don't get the 'philosophical' argument. – H H Mar 03 '09 at 23:13
  • 1
    @Henk; you don't ever **change** an int; you create a new one. That is the immutability. – Marc Gravell Mar 03 '09 at 23:16
  • 2
    Do you EVER sleep?? XDDD – Jorge Córdoba Mar 03 '09 at 23:31
  • 4
    Real programmers don't sleep, they just gives up the rest of a time slot now and then. ;) – Guffa Mar 03 '09 at 23:36
  • Also what about passing structs as ref? Is it a con too? The only problem I see with that was, it was slower than just passing. – Joan Venge Mar 03 '09 at 23:39
  • @Joan: Passing structs by ref is okay where it makes sense. That's not changing the contents *within* a particular value, it's just changing the value of a variable to a new value. – Jon Skeet Mar 03 '09 at 23:50
11

I use mutable structs often in my (performance critical) project - and I don't run into problems, because I understand the implications of copy semantics. As far as I can tell, the main reason people advocate immutable structs is so that people who don't understand the implications can't get themselves in trouble.

That's not such a terrible thing - but we're in danger now of it becoming "the gospel truth", when in fact there are times where it is legitimately the best option to make a struct mutable. Like with all things, there are exceptions to the rule.

MattDavey
  • 8,897
  • 3
  • 31
  • 54
  • 1
    Keep in mind that most of the recommedations come from the "Framework Design Guidelines". That means their target audience is people writing APIs for other people to use. In this scenario the ability to write the no-op "myClass.SomeValue.AProperty = 5" is just inviting failure. – Jonathan Allen Feb 19 '11 at 17:08
  • @JonathanAllen: The remedies for that would be: (1) Allow properties and methods to be tagged with an attribute indicating whether they should be usable on structs in read-only contexts; (2) Have structs expose fields rather than self-mutating properties. I can't think of any framework structs which expose self-mutating properties that wouldn't have been better implemented using exposed fields. Note that a compiler wouldn't have to guess whether `myClass.SomeValue.AField = 5` would try to mutate `myClass.SomeValue`. It could tell that it would. – supercat Sep 29 '12 at 16:53
  • @JonathanAllen: It really irks me that there's no way to override the compiler's "guesses" that a property setter will modify a struct (and should be forbidden in read-only contexts), or that other methods won't (and should thus be allowed)--especially since there are some cases where it would be useful for a struct to wrap an immutable reference to a mutable class, and have property setters act on the class item, but one ends up having to create unnecessary GC pressure by using a class instead to allow the property accessors to work. – supercat Sep 29 '12 at 16:56
6

There is nothing cheaper to manipulate than a mutable struct, which is why you often see it in high performance code like the graphics processing routines.

Unfortunately mutable structs don't play well with objects and properties, it is way too easy to modify a copy of a stuct instead of the struct itself. Thus they aren't appropriate for most of your code.

P.S. To avoid the cost of copying mutable structs, they are usually stored and passed in arrays.

Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
  • IMHO, the fact that properties don't work well with structs is a fault of the way properties work, and the lack of any standard means of returning a reference to an element of a value-type array. If the system had a standard ArrayIndexer(Of T) type which contained a reference to an array and an index to it, value-type properties could work much better. – supercat Feb 19 '11 at 04:47
  • Wouldn't work, you still run the risk that the whole struct will be copied onto the stack by the compiler or the JIT optimizer. – Jonathan Allen Feb 19 '11 at 17:10
  • For the idea to be really helpful, the compiler and JITter would have to know and honor the semantics of the ArrayIndexer(Of T) type, which should provide a means of accessing the appropriate element of the array without exposing the array itself. This could perhaps be done by having an AccessElement(int index, ref PT1 p1, ref PT2 p2, delegate(ref T item, ref PT1 p1, ref PT2 p2)) method which would accept a callback function to which the particular array item should be passed. Code would be ugly in the absence of compiler support, but with support it should be fine. – supercat Oct 28 '11 at 22:50
  • @supercat It took awhile, but that has now been fixed. If you make the property read-only and use a `ref` return instead of a normal one, then the underlying struct field can be mutated in the way you would expect. – Jonathan Allen Mar 14 '22 at 06:28
5

You have asked for the pros and cons of not following the guideline that structs should be immutable.

Cons: The cons are well covered in existing answers, and most problems described are due to the same cause - unexpected behaviour due to structs' value semantics.

Pros: The main pro of using mutable structs can be performance. Obviously, this advice comes with all the usual caveats about optimisations: make sure that part of your code needs to be optimised and make sure any changes do actually optimise your code's performance via profiling.

For an excellent article discussing when you might want to use mutable structs, see Rico Mariani's Performance Quiz on Value-Based Programming (or more specifically, the answers).

Ergwun
  • 12,579
  • 7
  • 56
  • 83
5

The technical reason is that mutable structs appear to be able to do things that they don't actually do. Since the design-time semantics are the same as reference types, this becomes confusing for developers. This code:

public void DoSomething(MySomething something)
{
    something.Property = 10;
}

Behaves quite differently depending on if MySomething is a struct or a class. To me, this is a compelling, but not the most compelling reason. If you look at DDD's Value Object, you can see the connection to how structs should be treated. A value object in DDD can be best represented as a value type in .Net (and therefore a struct). Because it has no identity, it can't change.

Think of this in terms of something like your address. You can "change" your address, but the address itself hasn't changed. In fact, you have a new address assigned to you. Conceptually, this works, because if you actually changed your address, your roommates would have to move too.

Michael Meadows
  • 27,796
  • 4
  • 47
  • 63
4

A struct should generally represent a single unity of some kind. As such it doesn't make much sense to change one of the properties of the value, it makes more sense to create a completely new value if you want a value that is different somehow.

The semantics gets simpler when using immutable structs, and you avoid pitfalls like this:

// a struct
struct Interval {
   int From { get; set; }
   int To { get; set; }
}

// create a list of structs
List<Interval> intervals = new List<Interval>();

// add a struct to the list
intervals.Add(new Interval());

// try to set the values of the struct
intervals[0].From = 10;
intervals[0].To = 20;

The result is that the struct in the list is not changed at all. The expression Interval[0] copies the value of the struct from the list, then you change the property of the temporary value, but the value is never put back in the list.

Edit: Changed the example to use a list instead of an array.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • This has killed me many times when dealing with legacy code! Implementing structs as immutable value objects prevents exactly this situation. – Michael Meadows Mar 03 '09 at 22:52
  • Thanks Guffa, but are you sure? AFAIK array is the only collection that allows this. – Joan Venge Mar 03 '09 at 22:54
  • Yes, you are right, I tested this and it actually works with an array. I edited the example to use a list. – Guffa Mar 03 '09 at 23:29
  • Thanks Guffa, it's all good :) – Joan Venge Mar 03 '09 at 23:31
  • `Interval` is a struct, so you can't use the indexer for that. I assume you mean `intervals[0]` and not `Interval[0]`. Even then the compiler doesn't allow the assignment as it isn't a variable. You need to assign a local variable like `var i = intervals[0];` first. Changing this item will not change the item in the list. – Brian Rasmussen Mar 08 '10 at 08:55
  • @Brian: Yes, Interval[0] was a typo, thanks for noticing. More recent versions of the compiler might do some extra checking and notice that changing the properties doesn't actually change anything, but earlier versions didn't. Even if the compilers get better at noticing such problems, there will always be situations that they can't warn you about. – Guffa Mar 08 '10 at 13:08
2

When you copy structs around you copy their contents, so if you modify a copied version the "original" will not be updated.

This is a source for errors, since even if you know that you fall into the trap of copying a struct (just by passing it to a method) and modifying the copy.

Just happened to me again last week, kept me an hour searching for a bug.

Keeping structs immutable prevents that ...

Other than that you need to make sure you have a really good reason to use structs in the first place - "optimization" or "I want something that allocates quickly on the stack" does not count as an answer. Marshaling or things where you depend on layout - ok, but you should not typically keep those structs around for very long, they are values not objects.

froh42
  • 5,190
  • 6
  • 30
  • 42
  • Thanks, my main reliance on structs comes from the fact that frameworks like xna, etc uses them for pretty much everything, so even if I need to write my own, I just use structs. – Joan Venge Mar 03 '09 at 23:02
2

the reason you should make structs immutable is that they're ValueTypes, meaning that they are copied every time you pass them to a method.

So if, for example, you had a property that returned a struct, modifying the value of a field on that struct would be worthless, because the getter would return a copy of the struct, rather than a reference to the struct. I've seen this done in code, and it's often difficult to catch.

If you design your structs for immutability, you help the programmer avoid these mistakes.

Randolpho
  • 55,384
  • 17
  • 145
  • 179