digitalmars.D.learn - Immutability and arrays
- rumbu (18/18) Dec 14 2021 I am trying to understand why in this two different cases (Simple
- WebFreak001 (7/29) Dec 14 2021 there are special cases in the compiler for values that have no
- Stanislav Blinov (4/22) Dec 14 2021 Because is(typeof(immutable(ComplexStruct).x) ==
- rumbu (34/37) Dec 14 2021 This means that the only way to write a generic function which
- Steven Schveighoffer (27/51) Dec 14 2021 I know there have been several answers as to what the rules are, I want
- Stanislav Blinov (35/38) Dec 14 2021 Not exactly. One of the problems seems to be a genuine bug:
- Steven Schveighoffer (8/11) Dec 14 2021 I agree slice-assign should work here with a copy ctor. What I think is
- Steven Schveighoffer (5/18) Dec 14 2021 Er... scratch that, this isn't construction, it should use opAssign.
- Steven Schveighoffer (10/14) Dec 14 2021 Simple proof that it is a bug:
- rumbu (8/24) Dec 14 2021 Thank you everybody, especially to Steve for the detailed
I am trying to understand why in this two different cases (Simple and Complex), the compiler behaviour is different. ```d struct SimpleStruct { int x;} struct ComplexStruct { int[] x; } void main() { SimpleStruct[] buf1; immutable(SimpleStruct)[] ibuf1; buf1[0 .. 10] = ibuf1[0 .. 10]; //this works ComplexStruct[] buf2; immutable(ComplexStruct)[] ibuf2; buf2[0 .. 10] = ibuf2[0 .. 10]; //error cannot implicitly convert expression `ibuf2[0..10]` of type `immutable(ComplexStruct)[]` to `ComplexStruct[]` } ```
Dec 14 2021
On Tuesday, 14 December 2021 at 08:44:02 UTC, rumbu wrote:I am trying to understand why in this two different cases (Simple and Complex), the compiler behaviour is different. ```d struct SimpleStruct { int x;} struct ComplexStruct { int[] x; } void main() { SimpleStruct[] buf1; immutable(SimpleStruct)[] ibuf1; buf1[0 .. 10] = ibuf1[0 .. 10]; //this works ComplexStruct[] buf2; immutable(ComplexStruct)[] ibuf2; buf2[0 .. 10] = ibuf2[0 .. 10]; //error cannot implicitly convert expression `ibuf2[0..10]` of type `immutable(ComplexStruct)[]` to `ComplexStruct[]` } ```there are special cases in the compiler for values that have no mutable indirections: https://dlang.org/spec/const3.html#implicit_qualifier_conversionsValues that have no mutable indirections (including structs that don't contain any field with mutable indirections) can be implicitly converted across mutable, const, immutable, const shared, inout and inout shared.so that first struct may be implicitly converted between mutable/immutable/const because it doesn't contain any mutable indirections (mutable arrays/pointers/references)
Dec 14 2021
On Tuesday, 14 December 2021 at 08:44:02 UTC, rumbu wrote:I am trying to understand why in this two different cases (Simple and Complex), the compiler behaviour is different. ```d struct SimpleStruct { int x;} struct ComplexStruct { int[] x; } void main() { SimpleStruct[] buf1; immutable(SimpleStruct)[] ibuf1; buf1[0 .. 10] = ibuf1[0 .. 10]; //this works ComplexStruct[] buf2; immutable(ComplexStruct)[] ibuf2; buf2[0 .. 10] = ibuf2[0 .. 10]; //error cannot implicitly convert expression `ibuf2[0..10]` of type `immutable(ComplexStruct)[]` to `ComplexStruct[]` } ```Because is(typeof(immutable(ComplexStruct).x) == immutable(int[])). Can't bind an array of immutable to array of mutable. This would require a deep copy, i.e. copy constructor.
Dec 14 2021
On Tuesday, 14 December 2021 at 12:13:23 UTC, Stanislav Blinov wrote:Because is(typeof(immutable(ComplexStruct).x) == immutable(int[])). Can't bind an array of immutable to array of mutable. This would require a deep copy, i.e. copy constructor.This means that the only way to write a generic function which copies an array of immutable elements to another array is this: ```d void copy10(T)(T[] dst, immutable(T)[] src) { static if (is(immutable(T): T)) dst[0..10] = src[0..10]; else dst[0..10] = cast(T[])src[0..10]; // or better a deep copy } ``` Btw, tried to give ComplexStruct a some copy constructors (casting away immutable is just for the example, I know it's not the way to do it). ```d struct ComplexStruct { int[] x; this(ref return scope ComplexStruct another) { this.x = another.x; } this(ref return scope immutable(ComplexStruct) another) { this.x = cast(int[])(another.x); } } ``` Still slice assignment does not work. I think I will drop immutability, it's too complicated to work with.
Dec 14 2021
On 12/14/21 3:44 AM, rumbu wrote:I am trying to understand why in this two different cases (Simple and Complex), the compiler behaviour is different. ```d struct SimpleStruct { int x;} struct ComplexStruct { int[] x; } void main() { SimpleStruct[] buf1; immutable(SimpleStruct)[] ibuf1; buf1[0 .. 10] = ibuf1[0 .. 10]; //this works ComplexStruct[] buf2; immutable(ComplexStruct)[] ibuf2; buf2[0 .. 10] = ibuf2[0 .. 10]; //error cannot implicitly convert expression `ibuf2[0..10]` of type `immutable(ComplexStruct)[]` to `ComplexStruct[]` } ```I know there have been several answers as to what the rules are, I want to answer why the rules are there. In the first case, you have a simple struct which has a single integer in it. When copying that struct, you have no indirections (pointers), which means that adjusting the mutability is allowed: ```d immutable s = SimpleStruct(5); SimpleStruct s2 = s; // ok, we are making a copy of everything ``` In the second case, the `int[]` contains a pointer. So if you made a copy of that, you cannot change the mutability of the type, because now it would have a mutable pointer to immutable data: ```d immutable s = ComplexStruct([5]); ComplexStruct s2 = s; // error, cannot implicitly convert, there is an indirection ``` The WHY is this: let's say the above was allowed, `s2` is mutable, which means `s2.x` is mutable. Now I can do: ```d s2.x[0] = 6; ``` And all of a sudden, immutable data has changed! This cannot be allowed, which is why you can't copy the struct. All the other problems you are having are deriving from this problem. -Steve
Dec 14 2021
On Tuesday, 14 December 2021 at 15:28:30 UTC, Steven Schveighoffer wrote:All the other problems you are having are deriving from this problem.Not exactly. One of the problems seems to be a genuine bug: ```d struct S { int[] x; // doesn't even participate here, neither would postblit this(ref return scope inout S other) { x = other.x.dup; } void opAssign(ref return scope inout S other) { x = other.x.dup; } } void main() { immutable(S)[] src = [S([1, 2]), S([3, 4])]; auto dst = new S[src.length]; //dst[0 .. $] = src[0 .. $]; // this fails to compile even with opAssign defined // this works: foreach (i, ref it; dst) it = src[i]; } ``` Spec: https://dlang.org/spec/arrays.html#array-copying...contents of the array are the target of the assignment...Per that wording, slice assignment should perform the equivalent of that foreach loop (after overlap checks, etc.). It doesn't, just tries implicit conversion, and fails. Now, since we have copy ctors, slice assignment should, ostensibly, attempt to copy-assign elements (i.e. absent opAssign, try the copy ctor first).
Dec 14 2021
On 12/14/21 10:53 AM, Stanislav Blinov wrote:Now, since we have copy ctors, slice assignment should, ostensibly, attempt to copy-assign elements (i.e. absent opAssign, try the copy ctor first).I agree slice-assign should work here with a copy ctor. What I think is happening is that it's still very much built on memcpy + postblit (so much of the array runtime is still magic functions). postblit doesn't work because it first makes a copy of the data AS-IS, which means it's still immutable, and only after the postblit would it be mutable. -Steve
Dec 14 2021
On 12/14/21 11:14 AM, Steven Schveighoffer wrote:On 12/14/21 10:53 AM, Stanislav Blinov wrote:Er... scratch that, this isn't construction, it should use opAssign. Again, probably because memcpy+postblit is used by the runtime. If not reported, it should be. -SteveNow, since we have copy ctors, slice assignment should, ostensibly, attempt to copy-assign elements (i.e. absent opAssign, try the copy ctor first).I agree slice-assign should work here with a copy ctor. What I think is happening is that it's still very much built on memcpy + postblit (so much of the array runtime is still magic functions). postblit doesn't work because it first makes a copy of the data AS-IS, which means it's still immutable, and only after the postblit would it be mutable.
Dec 14 2021
On 12/14/21 11:19 AM, Steven Schveighoffer wrote:Er... scratch that, this isn't construction, it should use opAssign. Again, probably because memcpy+postblit is used by the runtime. If not reported, it should be.Simple proof that it is a bug: ```d immutable (ComplexStruct)[] arr; ComplexStruct[] arr2; arr2[0] = arr[0]; // ok arr2[] = arr[]; // error ``` If you can copy one element, you should be able to copy all the elements. -Steve
Dec 14 2021
On Tuesday, 14 December 2021 at 16:21:03 UTC, Steven Schveighoffer wrote:On 12/14/21 11:19 AM, Steven Schveighoffer wrote:Thank you everybody, especially to Steve for the detailed explanation. It seems that every post I put in the learn forum, results in a bug report :) https://issues.dlang.org/show_bug.cgi?id=22601 https://issues.dlang.org/show_bug.cgi?id=22600Er... scratch that, this isn't construction, it should use opAssign. Again, probably because memcpy+postblit is used by the runtime. If not reported, it should be.Simple proof that it is a bug: ```d immutable (ComplexStruct)[] arr; ComplexStruct[] arr2; arr2[0] = arr[0]; // ok arr2[] = arr[]; // error ``` If you can copy one element, you should be able to copy all the elements. -Steve
Dec 14 2021