www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Unintentional sharing?

reply Andy Valencia <dont spam.me> writes:
I was using instance initialization which allocated a new object. 
  My intention was this initialization would happen per-instance, 
but all instances appear to share the same sub-object?  That is, 
f1.b and f2.b appear to point to a single object?  Obviously I 
moved the new into the initializer code, but I hadn't appreciated 
how initial instance values were calculated once.  Interestingly, 
this makes it similar to how Python calculates default argument 
values for functions.

class Bar {
     int z = 3;
}

class Foo {
     auto b = new Bar();
}

void
main() {
     import std.stdio : writeln;

     auto f1 = new Foo(), f2 = new Foo();
     f1.b.z = 0;
     writeln(f2.b.z);
}
Jun 06
next sibling parent evilrat <evilrat666 gmail.com> writes:
On Thursday, 6 June 2024 at 17:49:39 UTC, Andy Valencia wrote:
 I was using instance initialization which allocated a new 
 object.  My intention was this initialization would happen 
 per-instance, but all instances appear to share the same 
 sub-object?  That is, f1.b and f2.b appear to point to a single 
 object?  Obviously I moved the new into the initializer code, 
 but I hadn't appreciated how initial instance values were 
 calculated once.  Interestingly, this makes it similar to how 
 Python calculates default argument values for functions.

 class Bar {
     int z = 3;
 }

 class Foo {
     auto b = new Bar();
 }

 void
 main() {
     import std.stdio : writeln;

     auto f1 = new Foo(), f2 = new Foo();
     f1.b.z = 0;
     writeln(f2.b.z);
 }
What you are seeing here is indeed sharing reference. It happens because type initializer sets fields after memory allocation but before constructor call, and so since it is using value known at compile time all instances will have share same reference. https://dlang.org/spec/class.html#constructors
Jun 06
prev sibling next sibling parent Nick Treleaven <nick geany.org> writes:
On Thursday, 6 June 2024 at 17:49:39 UTC, Andy Valencia wrote:
 I was using instance initialization which allocated a new 
 object.  My intention was this initialization would happen 
 per-instance, but all instances appear to share the same 
 sub-object?  That is, f1.b and f2.b appear to point to a single 
 object?  Obviously I moved the new into the initializer code, 
 but I hadn't appreciated how initial instance values were 
 calculated once.  Interestingly, this makes it similar to how 
 Python calculates default argument values for functions.

 class Bar {
     int z = 3;
 }

 class Foo {
     auto b = new Bar();
 }

 void
 main() {
     import std.stdio : writeln;

     auto f1 = new Foo(), f2 = new Foo();
     f1.b.z = 0;
     writeln(f2.b.z);
 }
This is a long standing issue: https://issues.dlang.org/show_bug.cgi?id=2947 I think with the next edition we can disallow (tail) mutable initializers for fields (and TLS globals too).
Jun 06
prev sibling parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Thu, Jun 06, 2024 at 05:49:39PM +0000, Andy Valencia via Digitalmars-d-learn
wrote:
 I was using instance initialization which allocated a new object.  My
 intention was this initialization would happen per-instance, but all
 instances appear to share the same sub-object?  That is, f1.b and f2.b
 appear to point to a single object?
[...] Yes, if you want a per-instance object, you need to do it in this(), not in the initializer. --T
Jun 06