views:

756

answers:

11

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);
+4  A: 

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.

Stefan Steinegger
You can create a simple struct like struct Foo { public int Bar; }. The question is not if you can do this, but if Foo is mutable or not.
Daniel Brückner
The System.Drawing.Point struct, for example, isn't immutable.
ChrisW
Ok, didn't know that there are any mutable structs in the framework. Correct my answer, thanks.
Stefan Steinegger
I can rewrite point.X = 42; as point = point.SetX(42); and if I can always do this, I might consider the Point struct immutable even if the interface does not show this very well.
Daniel Brückner
+6  A: 

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.

pgb
I am aware of that, but the question is if being a value type implies being immutable.
Daniel Brückner
It does not imply it, since it's up to the user to implement the inmutability. You can have a Point class that is inmutable, or implement is as mutable.
pgb
I still think you cannot. Could you give an example where modifing a struct with myStruct.DoStuuf() cannot be explained as myStruct = myStruct.DoStuff()?
Daniel Brückner
This happens because you are reusing the variable myStruct.If DoStuff modifies the same instance, then the assignment doesn't do anything (it copies over the same). If DoStuff yields another modified instance, you assign it, and overwrite the old myStruct memory space with it.
pgb
This is what I mean - you can explain modifying a struct with overwriting a struct with another struct returned from a method. Hence you could consider structs to be immutable. The above is obviously not true for reference types.
Daniel Brückner
If myStruct were a reference to a reference type (the sole reference in the system), the same would happen... You will have a newer object, referenced by the same variable.
pgb
+3  A: 

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.

mquander
Of course, this is quite a theoretical question and the thing with ref and boxing - I didn't yet figure that out completly. I tend to say ref is no problem because you obtain a reference to the variable, not to the contained value. Boxing seems a bit harder and I am still thinking about it.
Daniel Brückner
Your argument for ref doesn't make sense. Yes, you obtain a reference to it, but the value you're modifying is still a value-type.
jalf
I don't quite understand what you're talking about in regard to ref either. Here is the object: I have obtained a reference to it. I can change it, which changes the values associated with that very same object in memory. How is this "immutable" in any sense of the word? It's acting just like any reference type at this point.
mquander
Given the method static void Bar(ref int arg) { arg = 42; } and int foo = 1; Bar(foo);. This will modify foo, but it should surly not indicate that int is mutable. This is because you obtain a reference to the variable foo, not the contained int value.
Daniel Brückner
@daniel: if you don't really understand references then you don't understand the real issues of mutability. have a little experience in lowlevel languages (C) and you'll see the problems. then check a sane hilevel language (Scheme and Lua are great for this), and you'll se how immutability helps.
Javier
I have written code in many languages from Assembler to Prolog. If you think I missed something, give an example for the question I am going to add to my original question.
Daniel Brückner
+3  A: 

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.

ChrisW
Yes, it has the setter, but I could rewrite point.X = 42; as point = point.SetX(42) - the question is if this is always possible. If yes, you could consider the struct to be immutable (but with an interface that does not express this immutability very well).
Daniel Brückner
A: 

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.

David Cooper
+2  A: 

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.

jalf
Maybe the best answer up to now. But I think this are two concepts - mutability of a variable and mutability of the value of a variable. Going to think about it... +1
Daniel Brückner
Added another example
jalf
I have rewritten it ... I modified your example a bit - if I introduced something you didn't want, please undo this, but I believe you really wanted to set the field to 43.
Daniel Brückner
Actually, the "set" function was wrong. It should obviously just set the private "i" field and nothing else. No return values.
jalf
I removed your example again and modified my post a bit to make my point clearer. Perhaps you misread my example because of the above error?What I wanted to show is that if structs are immutable, the compiler would have to instantiated a new S object to overwrite the one I explicitly created on the first line in Foo(). But there is no way for the compiler to create an an instance of S where the "i" field = 43, so it can't just arbitrarily create new instances. It has to modify the existing one instead, because that's the only way 'i' can ever be set to 43.
jalf
I forgot to add something to my edit - I am not talking about letting the compiler rewrite the code. I was just talking about manual redesigin of the struct and its interface, hence the big differences between your example and my rewritten version.
Daniel Brückner
But what does that prove? Yes, we can always rewrite our code to use immutable structs, but that doesn't mean every struct *is* immutable. :)
jalf
A: 

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.

Aaron Digulla
You said, "Now, Point is immutable etc.", but that's not a good example: Point is *not* immutable.
ChrisW
You are wrong, p2 will not equal p after the method call p.moveTo(5,7) if Point is a value type.
Daniel Brückner
@Daniel: I'm right since Point is not a value type in my example. ("in-place operation")
Aaron Digulla
@ChrisW: It is since there are no methods to modify it in-place.
Aaron Digulla
To clear up the mess: I missed the ".net". I'm talking about OO languages in general, not .net in particular.
Aaron Digulla
"This is different. i is not immutable, 5 is." This is a good point. The variable itself is mutable, but not the value of the variable. So against you answer "No, they are not." I still believe they are. Can you give an example where myStruct.DoStuff() cannot be explained as myStruct = myStruct.DoStuff()?
Daniel Brückner
When myStruct.DoStuff() changes the internal state of the object, anyone holding a reference to that object will be affected. If all methods to the object return a new object, then this internal state can't be changed from outside, making it effective immutable like the "5". In the same way adding 1 to 5 doesn't change the 5.
Aaron Digulla
That said, I don't know anything about structs in .Net :) So this is just how it works in general.
Aaron Digulla
Structs in .NET are value types like ints - so you cannot have a reference to the value of field or variable.
Daniel Brückner
@Daniel: "you cannot have a reference to the value", yes you can, see my answer :)
Lucas
A: 

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.

Michael Burr
That point is clear - reference types with value type semmantics must or at least should be designed immutable. From your statement "[...] This doesn't happen with value types: [...]" I conclude that you tend to agree on my conclusion - value types are immutable by definition because you cannot obtain a reference to a value, right?
Daniel Brückner
No - value types are not immutable by definition. In my example above, the `b.x = 2;` statement changes `b` - it just doesn't also change `b2`. That's a key difference between value types and ref types. I suppose you could look at it as if `b` got an entirely new object with a new value when it's changed, but that's not what's happening and I see nothing useful coming out of thinking about it that way.
Michael Burr
Now you got my point. I am currently thinking about myStruct.DoStuff(); rewritten as myStruct = myStruct.DoStuff(); because this makes the immutability of the struct obviously. My question could be rephrased - can you find an example where the mentioned transformation cannot be done or will not work?
Daniel Brückner
I see no technical problem with it.
Michael Burr
"can you find an example...?" Yes, if b and b2 where both declared as the interface IBar.
Lucas
+12  A: 

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 Method2(). 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 change. 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
IFoo 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

Lucas
I added this comment to several answers before. I could rewrite p.X = 5; as p = p.SetX(5);. If I can always do this - the value type semantics may allow this, but I don't know for sure - I could consider the struct to be immutable or equivalent to an immutable struct. So I rephrased the question - can I always do this transformation? If yes, this implies that structs are immutable because I can rewrite them in a way that makes the immutability obviouse.
Daniel Brückner
@Daniel: I'm not sure I follow you. If you can do p.X = 5 the type is mutable. If p2 = p1.SetX(5) does not change p1, and there is no way of changing p1, then it is immutable. Note that p = p.SetX(5) is replacing the value of p with a new one, not modifying the original value.
Lucas
You are absolutly right. And because p is a value type and you cannot have a reference to it, it should not matter if you modify the value stored in p or replace it with a modified version. If you find an example where this matters - maybe something involving ref parameters, boxing, or something I haven't even thought about (currently thinking about value type properties) - than I am wrong and structs are mutable. If I can always transform myStruct.ChangeStuff() into myStruct = myStruct.ChangeStuff(), than I can consider structs to be immutable.
Daniel Brückner
I think I finally understood what you mean! It was all in "p is a value type and you cannot have a reference to it", but you *can*, through boxing and interfaces. I have updated my answer.
Lucas
Internal immutability: This isn't an implementation detail - if you have a "mutable" struct with myStruct.ChangeState(), you can redesign the interface to myStruct = myStruct.GetCloneWithChangedState() making the struct "immutable". Both versions have the exactly same behavior using a different interface - so why should I call one version mutabe and one immutable?Boxing: I considered boxing a struct and then passing the object around, but all methods will operate on distinct unboxed values, hence the calls will not modify the shared value.
Daniel Brückner
Interface boxing: That is the true answer. Great! Value types are not immutable (I should better say "behave like immutable types") because you can share a reference to them by interface boxing. Thanks!
Daniel Brückner
A: 

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...]

Daniel Brückner
A: 

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...

Philippe Leybaert