www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Practical difference between a struct with const members and with

reply pineapple <meapineapple gmail.com> writes:
I'm mainly coming from languages that haven't got structs, let 
alone the kind of differentiation D offers between 
mutable/immutable/const/etc variables, so I'm still trying to 
work out just when to use each - What's different between these 
two examples, practically speaking? When would you use one over 
the other?

struct thing1{
     const int x, y;
}

struct thing2{
     int x, y;
}

Thanks!
Apr 09 2016
next sibling parent ag0aep6g <anonymous example.com> writes:
On 09.04.2016 18:07, pineapple wrote:
 What's different between these two examples, practically speaking? When
 would you use one over the other?

 struct thing1{
      const int x, y;
 }

 struct thing2{
      int x, y;
 }
In this case, const is practically the same as immutable. But immutable is the stricter qualifier. It's more self-documenting, and it may be simpler to utilize for the compiler in terms of optimizations. So I'd use immutable instead of const here. Differences between const and immutable manifest with indirections (pointers, arrays, classes, ...). The effect of const (or immutable) in thing1 is simple: you can't mutate its x or y after construction. Whereas thing2 allows mutation of its x and y. In code: ---- thing1 t1; t1 = thing1(1, 2); /* doesn't compile; the fields of t1 are immutable */ thing2 t2; t2 = thing2(1, 2); /* compiles */ ---- You can do the same with thing2 on a per-object basis: ---- immutable thing2 t2i; t2i = thing2(1, 2); /* doesn't compile; t2 is immutable */ ---- For a struct like thing1 that just has some value type members, I don't see a point in making all the members const/immutable. Making the instances const/immutable instead is more versatile. const is generally more useful when references are involved (pointers, arrays, classes, ...).
Apr 09 2016
prev sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, April 09, 2016 16:07:36 pineapple via Digitalmars-d-learn wrote:
 I'm mainly coming from languages that haven't got structs, let
 alone the kind of differentiation D offers between
 mutable/immutable/const/etc variables, so I'm still trying to
 work out just when to use each - What's different between these
 two examples, practically speaking? When would you use one over
 the other?

 struct thing1{
      const int x, y;
 }

 struct thing2{
      int x, y;
 }

 Thanks!
You'll be much happier if you don't make the members of a struct const or immutable. When they're const or immutable, you cannot mutate them, and you cannot mutate the struct as a whole (though other members can still be mutated). Not being able to mutate the const members is presumably what you want, but not being able to mutate the struct as a whole can be very annoying. For instance, if you have struct S { string foo; const int bar; } auto s = S("hello", 42); s.foo = "world"; s.bar = 77; // does not compile s = S("dlang", 99); // does not compile Presumably, you don't want bar to be able to change (which is why you made it const), so the first error is fine, but the second could be a big problem. By making a portion of the struct const, you can't assign to it anymore or do anything that would mutate it as a whole. You can just mutate its mutable members directly. Contrast this with struct S { string foo; int bar; } auto s = S("hello", 42); s.foo = "world"; s.bar = 77; // compiles s = S("dlang", 99); // compiles const t = S("str", 12); t.foo = "ing"; // does not compile t.bar = 7; // does not compile t = S("duck", 2); // does not compile This version of S can still be const if you want it to be, but it doesn't have to be - though obviously, bar can now be mutated if the struct as a whole is not const. The way to fix that is to provide a getter but not a setter. eg. struct S { public: property string foo() const { return _foo; } property void foo(string value) { _foo = value; } property int bar() const { return _bar; } private: string _foo; int _bar; } auto s = S("hello", 42); s.foo = "world"; s.bar = 77; // does not compile s = S("dlang", 99); // compiles const t = S("str", 12); t.foo = "ing"; // does not compile t.bar = 7; // does not compile t = S("duck", 2); // does not compile Because foo provides both a getter and a setter, it can be mutated as before, but because bar only provides a setter, it can't be mutated even when the struct is mutable - but you can still replace its value when you assign a value to the entire struct. And of course, if an instance of the struct is marked as const, then you can't mutate any of it. So, this approach gives you full flexibility. Now, having const or immutable members for classes isn't generally a problem, beacause they're on the heap, and you don't normally assign a new value to a class (rather, you just assign a new value to a reference to that object so that the reference refers to a different object, but the previous object wasn't actually mutated). e.g. class C { this(string f, int b) { foo = f; bar = b; } string foo; const int bar; } auto c = new C("hello", 42); c.foo = "world"; c.bar = 77; // does not compile c = new C("dlang", 99); // compiles const d = new C("str", 12); d.foo = "ing"; // does not compile d.bar = 7; // does not compile d = new C("duck", 2); // does not compile So, having const members for classes usually works well, but because structs normally live on the stack rather than having that layer of indirection, giving them const members does tend to get in the way. - Jonathan M Davis
Apr 09 2016