Question

I frequently read that structs should be immutable - aren't they by definition?

Do you consider int to be immutable?

int i = 0;
i = i + 123;

Seems okay - we get a new int and assign it back to i. What about this?

i++;

Okay, we can think of it as a shortcut.

i = i + 1;

What about the struct Point?

Point p = new Point(1, 2);
p.Offset(3, 4);

Does this really mutate the point (1, 2)? Shouldn't we think of it as a shortcut for the following with Point.Offset() returning a new point?

p = p.Offset(3, 4);

The background of this thought is this - how can a value type with no identity be mutable? You have to look at it at least twice to determine if it changed. But how can you do this without an identity?

I don't want to complicate reasoning about this by considering ref parameters and boxing. I am also aware that p = p.Offset(3, 4); expresses immutability much better than p.Offset(3, 4); does. But the question remains - aren't value types immutable by definition?

UPDATE

I think there are at least two concepts involved - the mutability of a variable or field and the mutability of the value of a variable.

public class Foo
{
    private Point point;
    private readonly Point readOnlyPoint;

    public Foo()
    {
        this.point = new Point(1, 2);
        this.readOnlyPoint = new Point(1, 2);
    }

    public void Bar()
    {
        this.point = new Point(1, 2);
        this.readOnlyPoint = new Point(1, 2); // Does not compile.

        this.point.Offset(3, 4); // Is now (4, 6).
        this.readOnlyPoint.Offset(3, 4); // Is still (1, 2).
    }
}

In the example we have to fields - a mutable one and a immutable one. Because a value type field contains the whole value, a value type stored in a immutable field must be immutable, too. I am still quite suprised by the result - I did not exspect the readonly field to remain unmodified.

Variables (besides constants) are allways mutable, hence they imply no restriction on the mutability of value types.


The answer seems not to be that straight forward so I will rephrase the question.

Given the following.

public struct Foo
{
    public void DoStuff(whatEverArgumentsYouLike)
    {
        // Do what ever you like to do.
    }

    // Put in everything you like - fields, constants, methods, properties ...
}

Can you give a completed version of Foo and an usage example - that may include ref parameters and boxing - so that it is not possible to rewrite all occurences of

foo.DoStuff(whatEverArgumentsYouLike);

with

foo = foo.DoStuff(whatEverArgumentsYouLike);
Was it helpful?

Solution

An object is immutable if its state doesn’t change once the object has been created.

Short answer: No, value types are not immutable by definition. Both structs and classes can be either mutable or immutable. All four combinations are possible. If a struct or class has non-readonly public fields, public properties with setters, or methods which set private fields, it is mutable because you can change its state without creating a new instance of that type.


Long answer: First of all, the question of immutability only applies to structs or classes with fields or properties. The most basic types (numbers, strings, and null) are inherently immutable because there is nothing (field/property) to change about them. A 5 is a 5 is a 5. Any operation on the 5 only returns another immutable value.

You can create mutable structs such as System.Drawing.Point. Both X and Y have setters which modify the struct's fields:

Point p = new Point(0, 0);
p.X = 5;
// we modify the struct through property setter X
// still the same Point instance, but its state has changed
// it's property X is now 5

Some people seem to confuse immutablity with the fact that value types are passed by value (hence their name) and not by reference.

void Main()
{
    Point p1 = new Point(0, 0);
    SetX(p1, 5);
    Console.WriteLine(p1.ToString());
}

void SetX(Point p2, int value)
{
    p2.X = value;
}

In this case Console.WriteLine() writes "{X=0,Y=0}". Here p1 was not modified because SetX() modified p2 which is a copy of p1. This happens because p1 is a value type, not because it is immutable (it isn't).

Why should value types be immutable? Lots of reasons... See this question. Mostly it's because mutable value types lead to all sorts of not-so-obvious bugs. In the above example the programmer might have expected p1 to be (5, 0) after calling SetX(). Or imagine sorting by a value which can later change. Then your sorted collection will no longer be sorted as expected. The same goes for dictionaries and hashes. The Fabulous Eric Lippert (blog) has written a whole series about immutability and why he believes it's the future of C#. Here's one of his examples that lets you "modify" a read-only variable.


UPDATE: your example with:

this.readOnlyPoint.Offset(3, 4); // Is still (1, 2).

is exactly the what Lippert referred to in his post about modifying read-only variables. Offset(3,4) actually modified a Point, but it was a copy of readOnlyPoint, and it was never assigned to anything, so it's lost.

And that is why mutable value types are evil: They let you think you are modifying something, when sometimes you are actually modifying a copy, which leads to unexpected bugs. If Point was immutable, Offset() would have to return a new Point, and you would not have been able to assign it to readOnlyPoint. And then you go "Oh right, it's read-only for a reason. Why was I trying to change it? Good thing the compiler stopped me now."


UPDATE: About your rephrased request... I think I know what you're getting at. In a way, you can "think" of structs as being internally immutable, that modifying a struct is that same as replacing it with a modified copy. It might even be what the CLR does internally in memory, for all I know. (That's how flash memory works. You cannot edit just a few bytes, you need to read a whole block of kilobytes into memory, modify the few you want, and write the whole block back.) However, even if they were "internally immutable", that is an implementation detail and for us developers as users of structs (their interface or API, if you will), they can be changed. We can't ignore that fact and "think of them as immutable".

In a comment you said "you cannot have a reference to the value of field or variable". You are assuming that every struct variable has a different copy, such that modifying one copy does not affect the others. That is not entirely true. The lines marked below are not replaceable if...

interface IFoo { DoStuff(); }
struct Foo : IFoo { /* ... */ }

IFoo otherFoo = new Foo();
IFoo foo = otherFoo;
foo.DoStuff(whatEverArgumentsYouLike); // line #1
foo = foo.DoStuff(whatEverArgumentsYouLike); // line #2

Lines #1 and #2 do not have the same results... Why? Because foo and otherFoo refer to the same boxed instance of Foo. Whatever is changed in foo in line #1 reflects in otherFoo. Line #2 replaces foo with a new value and does nothing to otherFoo (assuming that DoStuff() returns a new IFoo instance and does not modify foo itself).

Foo foo1 = new Foo(); // creates first instance
Foo foo2 = foo1; // create a copy (2nd instance)
IFoo foo3 = foo2; // no copy here! foo2 and foo3 refer to same instance

Modifying foo1 won't affect foo2 or foo3. Modifying foo2 will reflect in foo3, but not in foo1. Modifying foo3 will reflect in foo2 but not in foo1.

Confusing? Stick to immutable value types and you eliminate the urge of modifying any of them.


UPDATE: fixed typo in first code sample

OTHER TIPS

Mutability and value types are two separate things.

Defining a type as a value type, indicates that the runtime will copy the values instead of a reference to the runtime. Mutability, on the other hand, depends on the implementation, and each class can implement it as it wants.

You can write structs that are mutable, but it is best practice to make value types immutable.

For instance DateTime always creates new instances when doing any operation. Point is mutable and can be changed.

To answer your question: No, they are not immutable by definition, it depends on the case if they should be mutable or not. For instance, if they should serve as dictionary keys, they should be immutable.

If you take your logic far enough, then all types are immutable. When you modify a reference type, you could argue that you're really writing a new object to the same address, rather than modifying anything.

Or you could argue that everything is mutable, in any language, because occasionally memory that had previously been used for one thing, will be overwritten by another.

With enough abstractions, and ignoring enough language features, you can get to any conclusion you like.

And that misses the point. According to .NET spec, value types are mutable. You can modify it.

int i = 0;
Console.WriteLine(i); // will print 0, so here, i is 0
++i;
Console.WriteLine(i); // will print 1, so here, i is 1

but it is still the same i. The variable i is only declared once. Anything that happens to it after this declaration is a modification.

In something like a functional language with immutable variables, this would not be legal. The ++i would not be possible. Once a variable has been declared, it has a fixed value.

In .NET, that is not the case, there is nothing to stop me from modifying the i after it's been declared.

After thinking about it a bit more, here's another example that might be better:

struct S {
  public S(int i) { this.i = i == 43 ? 0 : i; }
  private int i;
  public void set(int i) { 
    Console.WriteLine("Hello World");
    this.i = i;
  }
}

void Foo {
  var s = new S(42); // Create an instance of S, internally storing the value 42
  s.set(43); // What happens here?
}

On the last line, according to your logic, we could say that we actually construct a new object, and overwrite the old one with that value. But that's not possible! To construct a new object, the compiler has to set the i variable to 42. But it's private! It is only accessible through a user-defined constructor, which explicitly disallows the value 43 (setting it to 0 instead), and then through our set method, which has a nasty side-effect. The compiler has no way of just creating a new object with the values it likes. The only way in which s.i can be set to 43 is by modifying the current object by calling set(). The compiler can't just do that, because it would change the behavior of the program (it would print to the console)

So for all structs to be immutable, the compiler would have to cheat and break the rules of the language. And of course, if we're willing to break the rules, we can prove anything. I could prove that all integers are equal too, or that defining a new class will cause your computer to catch fire. As long as we stay within the rules of the language, structs are mutable.

I don't want to complicate reasoning about this by considering ref parameters and boxing. I am also aware that p = p.Offset(3, 4); expresses immutability much better than p.Offset(3, 4); does. But the question remains - aren't value types immutable by definition?

Well, then you're not really operating in the real world, are you? In practice, the propensity of value types to make copies of themselves as they move between functions meshes well with immutability, but they aren't actually immutable unless you make them immutable, since, as you pointed out, you can use references to them just like anything else.

aren't value types immutable by definition?

No they're not: if you look at the System.Drawing.Point struct for example, it has a setter as well as a getter on its X property.

However it may be true to say that all value types should be defined with immutable APIs.

I think the confusion is that if you have a reference type that should act like a value type it's a good idea to make it immutable. One of the key differences between value types and reference types is that a change made through one name on a ref type can show up in the other name. This doesn't happen with value types:

public class foo
{
    public int x;
}

public struct bar
{
    public int x;
}


public class MyClass
{
    public static void Main()
    {
        foo a = new foo();
        bar b = new bar();

        a.x = 1;
        b.x = 1;

        foo a2 = a;
        bar b2 = b;

        a.x = 2;
        b.x = 2;

        Console.WriteLine( "a2.x == {0}", a2.x);
        Console.WriteLine( "b2.x == {0}", b2.x);
    }
}

Produces:

a2.x == 2
b2.x == 1

Now, if you have a type that you'd like to have value semantics, but don't want to actually make it a value type - maybe because the storage it requires is too much or whatever, you should consider that immutability is part of the design. With an immutable ref type, any change made to an existing reference produces a new object instead of change the existing one, so you get the value type's behavior that whatever value you're holding cannot be changed through some other name.

Of course the System.String class is a prime example of such behavior.

Last year I wrote a blog post regarding the problems you can run into by not making structs immutable.

The full post can be read here

This is an example of how things can go horribly wrong:

//Struct declaration:

struct MyStruct
{
  public int Value = 0;

  public void Update(int i) { Value = i; }
}

Code sample:

MyStruct[] list = new MyStruct[5];

for (int i=0;i<5;i++)
  Console.Write(list[i].Value + " ");
Console.WriteLine();

for (int i=0;i<5;i++)
  list[i].Update(i+1);

for (int i=0;i<5;i++)
  Console.Write(list[i].Value + " ");
Console.WriteLine();

The output of this code is:

0 0 0 0 0
1 2 3 4 5

Now let's do the same, but substitute the array for a generic List<>:

List<MyStruct> list = new List<MyStruct>(new MyStruct[5]); 

for (int i=0;i<5;i++)
  Console.Write(list[i].Value + " ");
Console.WriteLine();

for (int i=0;i<5;i++)
  list[i].Update(i+1);

for (int i=0;i<5;i++)
  Console.Write(list[i].Value + " ");
Console.WriteLine();

The output is:

0 0 0 0 0
0 0 0 0 0

The explanation is very simple. No, it's not boxing/unboxing...

When accessing elements from an array, the runtime will get the array elements directly, so the Update() method works on the array item itself. This means that the structs itself in the array are updated.

In the second example, we used a generic List<>. What happens when we access a specific element? Well, the indexer property is called, which is a method. Value types are always copied when returned by a method, so this is exactly what happens: the list's indexer method retrieves the struct from an internal array and returns it to the caller. Because it concerns a value type, a copy will be made, and the Update() method will be called on the copy, which of course has no effect on the list's original items.

In other words, always make sure your structs are immutable, because you are never sure when a copy will be made. Most of the time it is obvious, but in some cases it can really surprise you...

No, they are not. Example:

Point p = new Point (3,4);
Point p2 = p;
p.moveTo (5,7);

In this example moveTo() is an in-place operation. It changes the structure which hides behind the reference p. You can see that by look at p2: It's position will also have changed. With immutable structures, moveTo() would have to return a new structure:

p = p.moveTo (5,7);

Now, Point is immutable and when you create a reference to it anywhere in your code, you won't get any surprises. Let's look at i:

int i = 5;
int j = i;
i = 1;

This is different. i is not immutable, 5 is. And the second assignment doesn't copy a reference to the structure which contains i but it copies the content of i. So behind the scenes, something completely different happens: You get a complete copy of the variable instead of only a copy of the address in memory (the reference).

An equivalent with objects would be the copy constructor:

Point p = new Point (3,4);
Point p2 = new Point (p);

Here, the internal structure of p is copied into a new object/structure and p2 will contain the reference to it. But this is a pretty expensive operation (unlike the integer assignment above) which is why most programming languages make the distinction.

As computers become more powerful and get more memory, this distinction is going to go away because it causes an enormous amount of bugs and problems. In the next generation, there will only be immutable objects, any operation will be protected by a transaction and even an int will be a full blown object. Just like garbage collection, it will be a big step forward in program stability, cause a lot of grief in the first few years but it will allow to write dependable software. Today, computers just aren't fast enough for this.

No, value types are not immutable by definition.

First, I should better have asked the question "Do value types behave like immutable types?" instead of asking if they are immutable - I assume this caused a lot of confusion.

struct MutableStruct
{
    private int state;

    public MutableStruct(int state) { this.state = state; }

    public void ChangeState() { this.state++; }
}

struct ImmutableStruct
{
    private readonly int state;

    public MutableStruct(int state) { this.state = state; }

    public ImmutableStruct ChangeState()
    {
        return new ImmutableStruct(this.state + 1);
    }
}

[To be continued...]

To define whether a type is mutable or immutable, one must define what that "type" is referring to. When a storage location of reference type is declared, the declaration merely allocates space to hold a reference to an object stored elsewhere; the declaration does not create the actual object in question. Nonetheless, in most contexts where one talks about particular reference types, one will not be talking about a storage location which holds a reference, but rather the object identified by that reference. The fact that one can write to a storage location holding a reference to an object implies in no way that the object itself is mutable.

By contrast, when a storage location of value type is declared, the system will allocate within that storage location nested storage locations for each public or private field held by that value type. Everything about the value type is held in that storage location. If one defines a variable foo of type Point and its two fields, X and Y, hold 3 and 6 respectively. If one defines the "instance" of Point in foo as being the pair of fields, that instance will be mutable if and only if foo is mutable. If one defines an instance of Point as being the values held in those fields (e.g. "3,6"), then such an instance is by definition immutable, since changing one of those fields would cause Point to hold a different instance.

I think it is more helpful to think of a value type "instance" as being the fields, rather than the values they hold. By that definition, any value type stored in a mutable storage location, and for which any non-default value exists, will always be mutable, regardless of how it is declared. A statement MyPoint = new Point(5,8) constructs a new instance of Point, with fields X=5 and Y=8, and then mutates MyPoint by replacing the values in its fields with those of the newly-created Point. Even if a struct provides no way to modify any of its fields outside its constructor, there is no way a struct type can protect an instance from having all of its fields overwritten with the content of another instance.

Incidentally, a simple example where a mutable struct can achieve semantics not achievable via other means: Assuming myPoints[] is a single-element array which is accessible to multiple threads, have twenty threads simultaneously execute the code:

Threading.Interlocked.Increment(myPoints[0].X);

If myPoints[0].X starts out equal to zero and twenty threads perform the above code, whether simultaneously or not, myPoints[0].X will equal twenty. If one were to attempt to mimic the above code with:

myPoints[0] = new Point(myPoints[0].X + 1, myPoints[0].Y);

then if any thread read myPoints[0].X between the time another thread read it and wrote back the revised value, the results of the increment would be lost (with the consequence that myPoints[0].X could arbitrarily end up with any value between 1 and 20.

Objects/Structs are immutable when they are passed into a function in such a way as the data cannot be changed, and the returned struct is a new struct. The classic example is

String s = "abc";

s.toLower();

if the toLower function is written so they a new string is returned that replaces "s", it's immutable, but if the function goes letter by letter replacing the letter inside "s" and never declaring a "new String", it is mutable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top