Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can struct change their own fields?

Tags:

c#

struct

Consider the Foo struct as follows:

struct Foo
{
  public float X;
  public float Y;

  public Foo(float x, float y)
  {
    this.X = x;
    this.Y = y;
  }

  public void Change(float x)
  {
    this.X = x;
  }
}

I understand modifying the field in the constructor, that's perfectly logical to me and my understanding of structs as value, number-like immutable types.

However, since one can't do:

Foo bar = new Foo(1, 2);
bar.X = 5;

Why can one use:

Foo bar = new Foo(1, 2);
bar.Change(5);

EDIT: If structs are mutable, then why can't they be modified when in a list or returned from a property?

Cannot modify expression because it is not a variable

like image 447
Lazlo Avatar asked Jan 18 '11 04:01

Lazlo


1 Answers

Since one cannot do

Foo bar = new Foo(1, 2); 
bar.X = 5; 

Why can one use:

Foo bar = new Foo(1, 2); 
bar.Change(5); 

Your original question actually cannot be answered because it is predicated on a completely false assumption. Both code samples are perfectly legal, and so the question about why one is illegal is nonsensical. Let's move on to your follow-up question:

If structs are mutable, then why can't they be modified when in a list or returned from a property?

Because variables are mutable and values are immutable.

That's why they're called "variables", after all, because they can change.

When you say "bar.X = 5", "bar" is a local variable. Variables are allowed to change.

When you say "bar.Change(5)", "bar" is a local variable. Variables are allowed to change.

When you say "myArray[123].X = 5", "myArray[123]" is an array element and an array element is a variable. Variables are allowed to change.

When you say "myDictionary[123].X = 5", "myDictionary[123]" is not a variable. The value is returned from the dictionary, not a reference to the storage location. Since that is a value, not a variable, there is nothing there that can change, so the compiler does not allow it to change.

A subtle point is that when you attempt to change a field, the receiver must be a variable. If it is not a variable, it makes no sense; you are clearly attempting to mutate a variable and there's nothing there to mutate. When you call a method, the receiver must be a variable but what if you have a value? The method might not attempt to mutate anything, and so should be allowed to succeed. What the compiler actually does if the receiver of a method call on a struct is not a variable, then it makes a new temporary local variable and calls the method with that variable. So if you say: "myDictionary[123].Change(5);" that is the same as saying

var temp = myDictionary[123];
temp.Change(5);

Here "temp" is a variable, and the mutating method is allowed to change the temporary copy.

Is that now clear? The key takeaway here is variables can change.

like image 107
Eric Lippert Avatar answered Sep 20 '22 22:09

Eric Lippert