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