64

I was just curious to know why structs, strings etc are immutable? What is the reason for making them immutable and rest of the objects as mutable. What are the things that are considered to make an object immutable?

Is there any difference on the way how memory is allocated and deallocated for mutable and immutable objects?

djdd87
  • 67,346
  • 27
  • 156
  • 195
Sandeep
  • 7,156
  • 12
  • 45
  • 57
  • 17
    structs are not inherently immutable, that's usually a design goal while using them, though. – Marc Sep 20 '10 at 13:30
  • Not all reference types are mutable. In fact, some may not have any state whatsoever (such as strategy classes that implement a particular interface, like `IComparer`). – Dan Bryant Sep 20 '10 at 13:46
  • related: [why-are-mutable-structs-evil](http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil?rq=1) – nawfal Feb 13 '13 at 10:01
  • 2
    [This SO post](http://stackoverflow.com/questions/575977/choosing-between-immutable-objects-and-structs-for-value-objects) should have all you need. – cjk Sep 20 '10 at 13:31

5 Answers5

130

If this subject interests you, I have a number of articles about immutable programming at https://ericlippert.com/2011/05/26/atomicity-volatility-and-immutability-are-different-part-one/

I was just curious to know why structs, strings etc are immutable?

Structs and classes are not immutable by default, though it is a best practice to make structs immutable. I like immutable classes too.

Strings are immutable.

What is the reason for making them immutable and rest of the objects as mutable.

Reasons to make all types immutable:

  • It is easier to reason about objects that do not change. If I have a queue with three items in it, I know it is not empty now, it was not empty five minutes ago, it will not be empty in the future. It's immutable! Once I know a fact about it, I can use that fact forever. Facts about immutable objects do not go stale.

  • A special case of the first point: immutable objects are much easier to make threadsafe. Most thread safety problems are due to writes on one thread and reads on another; immutable objects don't have writes.

  • Immutable objects can be taken apart and re-used. For example, if you have an immutable binary tree then you can use its left and right subtrees as subtrees of a different tree without worrying about it. In a mutable structure you typically end up making copies of data to re-use it because you don't want changes to one logical object affecting another. This can save lots of time and memory.

Reasons to make structs immutable

There are lots of reasons to make structs immutable. Here's just one.

Structs are copied by value, not by reference. It is easy to accidentally treat a struct as being copied by reference. For example:

void M()
{
    S s = whatever;
    ... lots of code ...
    s.Mutate();
    ... lots more code ...
    Console.WriteLine(s.Foo);
    ...
}

Now you want to refactor some of that code into a helper method:

void Helper(S s)
{
    ... lots of code ...
    s.Mutate();
    ... lots more code ...
}

WRONG! That should be (ref S s) -- if you don't do that then the mutation will happen on a copy of s. If you don't allow mutations in the first place then all these sorts of problems go away.

Reasons to make strings immutable

Remember my first point about facts about immutable structures staying facts?

Suppose string were mutable:

public static File OpenFile(string filename)
{
    if (!HasPermission(filename)) throw new SecurityException();
    return InternalOpenFile(filename);
}

What if the hostile caller mutates filename after the security check and before the file is opened? The code just opened a file that they might not have permission to!

Again, mutable data is hard to reason about. You want the fact "this caller is authorized to see the file described by this string" to be true forever, not until a mutation happens. With mutable strings, to write secure code we'd constantly have to be making copies of data that we know do not change.

What are the things that are considered to make an object immutable?

Does the type logically represent something that is an "eternal" value? The number 12 is the number 12; it doesn't change. Integers should be immutable. The point (10, 30) is the point (10, 30); it doesn't change. Points should be immutable. The string "abc" is the string "abc"; it doesn't change. Strings should be immutable. The list (10, 20, 30) doesn't change. And so on.

Sometimes the type represents things that do change. Mary Smith's last name is Smith, but tomorrow she might be Mary Jones. Or Miss Smith today might be Doctor Smith tomorrow. The alien has fifty health points now but has ten after being hit by the laser beam. Some things are best represented as mutations.

Is there any difference on the way how memory is allocated and deallocated for mutable and immutable objects?

Not as such. As I mentioned before though, one of the nice things about immutable values is that something you can re-use parts of them without making copies. So in that sense, memory allocation can be very different.

Dour High Arch
  • 21,513
  • 29
  • 75
  • 90
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 2
    I think this what they say Crystal Clear...Thanks for such a detailed explanation... – Sandeep Sep 20 '10 at 17:19
  • 1
    the best thing I have read on immutability.. Though I like mutability more. – nawfal Feb 13 '13 at 10:08
  • Pretty enlightening, though I'm finding the reasoning about mutations (e.g. changing surnames) to contradict certain use cases, such as the binary tree example. Why should a binary tree be immutable? You could add or remove nodes, balance it, etc. I know it's still useful, e.g. in Roslyn to simplify multithreading in syntax trees, but it isn't logically consistent with your argument. – Gigi Oct 04 '14 at 13:53
  • @Gigi: What's is the most germane difference between adding a new node to an existing tree, and constructing a second tree that differs from the first tree only in the new node? Why do you think that the former is somehow conceptually more natural than the latter? – Eric Lippert Oct 05 '14 at 15:20
  • 1
    "If I have a queue with 3 items in it..." do you mean an immutable queue with 3 items in it? but, how is an immutable queue a queue? if you can't add or remove items, isn't it at best a list or enumerable? – Dave Cousineau Oct 06 '14 at 01:26
  • @Sahuagin: How is the number three even a number? You can't add one to it and still keep it being three! – Eric Lippert Oct 06 '14 at 05:49
  • 4
    @EricLippert let's say you have a stack of books. If you want to place another book on top of them, then it is much more natural to place the book on top of them, than to buy a copy of each book, stack them up in the same order, and then place the new book on top of those. Objects such as collections are often expected to change, and it is not intuitive to reason about them as something immutable. – Gigi Oct 06 '14 at 16:49
  • 7
    @gigi you *dont* buy a second copy of each book. Rather, when you add the third book you now have four stacks. The empty stack, the stack with one book on top of the empty stack, the stack with one book on top of that, and the stack with one book on top of that. It is precisely because you do *not* want to buy more books that immutability works! It is mutable data structures that require copying. You have it exactly backwards. – Eric Lippert Oct 06 '14 at 18:02
  • 2
    @EricLippert I see the light now. Thanks, you're always a fantastic teacher. – Gigi Oct 07 '14 at 17:27
  • @EricLippert But if the stack is mutable, and we add a third book to it, aren't the original 2 books still the same? We also don't "buy" them again? – NDUF May 16 '18 at 01:48
  • @NDUF: That's correct. But look at what you've lost. Suppose I have an immutable stack `s`. I can say `s1 = s.push(123);` and `s2 = s.push(456);` and **pushing s a second time did not change s1 at all**. – Eric Lippert May 16 '18 at 03:48
  • 2
    @NDUF: Let me put that another way. Suppose you have stack ABC and you want *both* stacks ABCX and ABCY in hand. If stack ABC is *mutable* then you have to make a copy, push X onto the original and Y onto the copy. If stack ABC is immutable then you can just copy the *reference* to ABC when you make your two new stacks. You don't have to copy the whole stack data structure. A structure which has this property is called "persistent". – Eric Lippert May 16 '18 at 14:17
  • @EricLippert yes that makes sense. Seems like it depends on your use case though, either way you need to be aware of what you're doing. I suppose having the stack immutable makes it behave more like a "local" variable when you pass it around, whether having it mutable would make it behave like a "global" variable, which can be modified from anywhere. This can be handy but also can have unexpected repercussions. – NDUF May 22 '18 at 01:46
9

Structs are not necessarily immutable, but mutable structs are evil.

Creating mutable structs can lead to all kinds of strange behavior in your application and, therefore, they are considered a very bad idea (stemming from the fact that they look like a reference type but are actually a value type and will be copied whenever you pass them around).

Strings, on the other hand, are immutable. This makes them inherently thread-safe as well as allowing for optimizations via string interning. If you need to construct a complicated string on the fly, you can use StringBuilder.

mfluehr
  • 2,832
  • 2
  • 23
  • 31
Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
  • He may have meant why structs and strings in the BCL are immutable (generally speaking, although I don't know of any mutable structs) rather than why *all* structs are immutable (which your answer correctly, um, corrects). – Anthony Pegram Sep 20 '10 at 13:34
  • @Anthony: Lots of `IEnumerator` implementations in the BCL are structs. E.g. http://msdn.microsoft.com/en-us/library/x854yt9s.aspx Don't expose one of them through a property, or suffer dire consequences. :) – Joren Sep 20 '10 at 14:06
5

The concepts of mutability and immutability have different meanings when applied to structs and classes. A key aspect (oftentimes, the key weakness) of mutable classes is if Foo has a field Bar of type List<Integer>, which holds a reference to a list containing (1,2,3), other code which has a reference to that same list could modify it, such that Bar holds a reference to a list containing (4,5,6), even if that other code has no access whatsoever to Bar. By contrast, if Foo had a field Biz of type System.Drawing.Point, the only way anything could modify any aspect of Biz would be to have write access to that field.

The fields (public and private) of a struct can be mutated by any code which can mutate the storage location in which the struct is stored, and cannot be mutated by any code which cannot mutate the storage location where it is stored. If all of the information encapsulated within a struct is held in its fields, such a struct can effectively combine the control of an immutable type with the convenience of a mutable type, unless the struct is coded in such a way as to remove such convenience (a habit which, unfortunately, some Microsoft programmers recommend).

The "problem" with structs is that when a method (including a property implementation) is invoked on a struct in a read-only context (or immutable location), the system copies the struct, performs the method on the temporary copy, and silently discards the result. This behavior has led programmers to put forth the unfortunate notion that the way to avoid problems with mutating methods is to have many structs disallow piecewise updates, when the problems could have been better avoided by simply replacing properties with exposed fields.

Incidentally, some people complain that when a class property returns a conveniently-mutable struct, changes to the struct don't affect the class from which it came. I would posit that's a good thing--the fact that the returned item is a struct makes the behavior clear (especially if it's an exposed-field struct). Compare a snippet using a hypothetical struct and property on Drawing.Matrix with one using an actual property on that class as implemented by Microsoft:

// Hypothetical struct
public struct {
  public float xx,xy,yx,yy,dx,dy;
} Transform2d;

// Hypothetical property of "System.Drawing.Drawing2d.Matrix"
public Transform2d Transform {get;}

// Actual property of "System.Drawing.Drawing2d.Matrix"
public float[] Elements { get; }

// Code using hypothetical struct
Transform2d myTransform = myMatrix.Transform;
myTransform.dx += 20;
... other code using myTransform

// Code using actual Microsoft property
float[] myArray = myMatrix.Elements;
myArray[4] += 20;
... other code using myArray

Looking at the actual Microsoft property, is there any way to tell whether the write to myArray[4] will affect myMatrix? Even looking at the page http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.matrix.elements.aspx is there any way to tell? If the property had been written using the struct-based equivalent, there would be no confusion; the property that returns the struct would return nothing more nor less than the present value of six numbers. Changing myTransform.dx would be nothing more nor less than a write to a floating-point variable which was unattached to anything else. Anyone who doesn't like the fact that changing myTransform.dx doesn't affect myMatrix should be equally annoyed that writing myArray[4] doesn't affect myMatrix either, except that the independence of myMatrix and myTransform is apparent, while the independence of myMatrix and myArray is not.

supercat
  • 77,689
  • 9
  • 166
  • 211
4

A struct type is not immutable. Yes, strings are. Making your own type immutable is easy, simply don't provide a default constructor, make all fields private and define no methods or properties that change a field value. Have a method that should mutate the object return a new object instead. There is a memory management angle, you tend to create a lot of copies and garbage.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

Structs can be mutable, but it's a bad idea because they have copy-semantics. If you make a change to a struct, you might actually be modifying a copy. Keeping track of exactly what has been changed is very tricky.

Mutable structs breed mistakes.

Craig Gidney
  • 17,763
  • 5
  • 68
  • 136