I frequently read that struct
s should be immutable - aren\'t they by definition?
Do you consider int
to be immutable?
int i
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.
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.
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 thatp = p.Offset(3, 4);
expresses immutability much better thanp.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.
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.
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