Please fix `.init` property

FeepingCreature feepingcreature at gmail.com
Mon Jan 8 10:57:52 UTC 2024


On Monday, 8 January 2024 at 05:22:20 UTC, Jonathan M Davis wrote:
> On Sunday, January 7, 2024 9:14:48 PM MST matheus via 
> Digitalmars-d wrote:
>> On Sunday, 7 January 2024 at 23:12:30 UTC, Hipreme wrote:
>> > ...
>>
>> For what it seems this happens only with arrays?
>>
>> This happened with for example int[] a; but when I tried with
>> other types (Non-arrays) like (string, int) this problem didn't
>> occur.
>>
>> Since like in this example string arrays are pointers, I think 
>> something was messed-up with the address.
>>
>> Matheus.
>
> Well, to an extent, the problem here is simply that the member 
> variable is a type with mutable indirections. The dynamic array 
> itself is just a pointer and a length, and mutating what it 
> points to like the OP's example doesn't actually mutate the 
> init value. In order to avoid that sort of mutation, 
> initializing the struct would need to give you a deep copy 
> rather than a shallow copy. Other member variables with mutable 
> indirections would have similar problems. For instance, all of 
> the assertions in this code pass:
>
> class C
> {
>     string foo = "foo";
> }
>
> struct S
> {
>     C c = new C;
> }
>
> void main()
> {
>     S s1;
>     assert(s1.c.foo == "foo");
>     assert(s1.c.foo == S.init.c.foo);
>
>     S s2;
>     s2.c.foo = "bar";
>     assert(s2.c.foo == "bar");
>
>     assert(s1.c.foo == "bar");
>     assert(S.init.c.foo == "bar");
>
>     assert((cast(void*)s1.c) is cast(void*)s2.c);
>     assert((cast(void*)s1.c) is cast(void*)S.init.c);
>
>     S s3;
>     s3.c = new C;
>     assert(s3.c.foo == "foo");
>
>     assert(s1.c.foo == "bar");
>     assert(s2.c.foo == "bar");
>     assert(S.init.c.foo == "bar");
>
>     assert((cast(void*)s1.c) is cast(void*)s2.c);
>     assert((cast(void*)s1.c) is cast(void*)S.init.c);
>
>     assert((cast(void*)s3.c) !is cast(void*)S.init.c);
> }
>
> The init value itself is never mutated, but what it points to 
> is. Someone could interpret it like the init value had mutated, 
> because what it's pointed to changed, and therefore, accessing 
> the values via init then results in different values that were 
> there at the start of the program, but technically, init itself 
> never changed.
>
> As such, the OP should expect that mutating values in the array 
> would affect any other variable when it's default-initialized, 
> and I don't think that it's realistic for it to work any other 
> way, because that requires making a deep copy of the init value 
> rather than a shallow copy, and that's not something that can 
> be done in the general case, because D doesn't have a mechanism 
> for that.
>
> However, in spite of all of that, something weird is going on 
> with the OP's example, because while variables that are created 
> after the element of the array has been mutated see that 
> mutation, the init value itself does not. So, it would appear 
> that the array in the init value itself somehow ends up 
> pointing to a different block of memory than the array does 
> when the struct is default initilaized. This example shows the 
> same problem
>
> struct S
> {
>     int[] arr = [1, 2, 3];
> }
>
> void main()
> {
>     S s1;
>     assert(s1.arr == [1, 2, 3]);
>     assert(s1.arr == S.init.arr);
>
>     S s2;
>     s2.arr[0] = 42;
>     assert(s2.arr == [42, 2, 3]);
>
>     assert(s1.arr == [42, 2, 3]);
>     assert(S.init.arr == [1, 2, 3]);
> }
>
> and if I were to change main to
>
> void main()
> {
>     import std.stdio;
>     writeln(S.init.arr.ptr);
>
>     S s1;
>     writeln(s1.arr.ptr);
>
>     S s2;
>     writeln(s2.arr.ptr);
>
>     writeln(S.init.arr.ptr);
> }
>
> running it on my computer results in
>
> C378D421000
> 2C3F08
> 2C3F08
> C378D421010
>
> So, for some reason, the ptr of the array in the init value has 
> a different address from the one in the struct's after they've 
> been default-initialized, but the struct's get the same pointer 
> value.
>
> This is in stark contrast to my previous example where the 
> member variable that was a class reference ended up with the 
> address being the same for both the init value and the 
> default-initialized structs. So, it would appear that the 
> compiler or runtime is doing something different with dynamic 
> arrays than it is with classes, and the behavior definitely 
> seems wrong, because it means that a default-initialized struct 
> does not match its init value - and this without worrying about 
> mutating anything. e.g. this assertion fails when it should 
> never fail
>
> struct S
> {
>     int[] arr = [1, 2, 3];
> }
>
> void main()
> {
>     S s;
>     assert(s is S.init);
> }
>
> So, I would say that there is definitely a bug here with 
> regards to init values when a struct has a member variable 
> which is a dynamic array.
>
> However, ultimately, that's not really what the OP seems to be 
> complaining about. Rather, he seems to be complaining about the 
> fact that if you mutate a member variable with mutable 
> indirections which were not null in the init value, then the 
> changes to those mutable indirections are visible via the init 
> value, and I don't think that that's something that's ever 
> going to change, because it would require that default 
> initialization do a deep copy rather than a shallow copy. 
> Obviously, it can be surprising if it's not something that 
> you've run into or thought through before, but D doesn't have a 
> generic way to do deep copies, and you wouldn't necessarily 
> want a deep copy in all cases anyway, so even if we could make 
> it do a deep copy, that wouldn't necessarily be a good change 
> for the language as a whole, though it would certainly fix the 
> issue that the OP ran into.
>
> Arguably, what the OP needs is a default constructor rather 
> than an init value, but that's not the way that D is designed, 
> and it would be problematic with some of its features to use 
> default construction instead (though this is far from the only 
> case where not having default construction for structs can be 
> annoying).
>
> - Jonathan M Davis

Force default values to be typed immutable?


More information about the Digitalmars-d mailing list