www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - I do not understand copy constructors

reply Learner <learner gmail.com> writes:
I have a structure like, used by other structures:

     struct A {
         int[] data;
         this(this) { data = data.dup; }
     }

I am trying to upgrade it to use copy constructor:

     struct A {
         int[] data
         this(ref return scope A rhs) { data = ths.data.dup; }
     }

     Generating an `inout` copy constructor for `struct B` failed, 
therefore instances of it are uncopyable

What is an `inout` copy constructor? What should I change in A?
Aug 12 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 12 August 2021 at 08:42:27 UTC, Learner wrote:
     struct A {
         int[] data
         this(ref return scope A rhs) { data = ths.data.dup; }
     }

     Generating an `inout` copy constructor for `struct B` 
 failed, therefore instances of it are uncopyable

 What is an `inout` copy constructor? What should I change in A?
When the compiler generates a copy constructor for a struct, it generates it with the following signature: ```d this(ref return scope inout(typeof(this)) src) inout ``` (Source: https://dlang.org/spec/struct.html#implicit-copy-constructors) Notice that both the `src` object and the object being constructed (`this`) are qualified with `inout`. `inout` is a special type qualifier that allows the same function to be used for mutable, `const`, and `immutable` arguments. To make this work, the compiler imposes heavy restrictions on what you can do with an `inout`-qualified object--only operations that are valid on mutable, `const`, *and* `immutable` objects are allowed for `inout`. (Source: https://dlang.org/spec/function.html#inout-functions) `A`'s copy constructor does not have any type qualifiers on its `rhs` argument or its `this` reference, so both default to mutable. In other words: `A`'s copy constructor can only be used to construct a mutable copy from a mutable original object. It *cannot* be used to construct an `inout` copy from an `inout` object. In order to make the generated copy constructor work, you need to give `A` a copy constructor that can copy `inout` objects. There are two possibilities here: 1. Make `A`'s copy constructor `inout`: `this(ref return scope inout A rhs) inout` 2. Make `A`'s copy constructor `const`: `this(ref return scope const A rhs) const`
Aug 12 2021
parent reply Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 09:14:02 UTC, Paul Backus wrote:
 On Thursday, 12 August 2021 at 08:42:27 UTC, Learner wrote:
     struct A {
         int[] data
         this(ref return scope A rhs) { data = ths.data.dup; }
     }

     Generating an `inout` copy constructor for `struct B` 
 failed, therefore instances of it are uncopyable

 What is an `inout` copy constructor? What should I change in A?
When the compiler generates a copy constructor for a struct, it generates it with the following signature: ```d this(ref return scope inout(typeof(this)) src) inout ``` (Source: https://dlang.org/spec/struct.html#implicit-copy-constructors) Notice that both the `src` object and the object being constructed (`this`) are qualified with `inout`. `inout` is a special type qualifier that allows the same function to be used for mutable, `const`, and `immutable` arguments. To make this work, the compiler imposes heavy restrictions on what you can do with an `inout`-qualified object--only operations that are valid on mutable, `const`, *and* `immutable` objects are allowed for `inout`. (Source: https://dlang.org/spec/function.html#inout-functions) `A`'s copy constructor does not have any type qualifiers on its `rhs` argument or its `this` reference, so both default to mutable. In other words: `A`'s copy constructor can only be used to construct a mutable copy from a mutable original object. It *cannot* be used to construct an `inout` copy from an `inout` object. In order to make the generated copy constructor work, you need to give `A` a copy constructor that can copy `inout` objects. There are two possibilities here: 1. Make `A`'s copy constructor `inout`: `this(ref return scope inout A rhs) inout` 2. Make `A`'s copy constructor `const`: `this(ref return scope const A rhs) const`
While option 2. is not working: struct A { int[] data; this(ref return scope const A rhs) const {} } Error: Generating an `inout` copy constructor for `struct A` failed, therefore instances of it are uncopyable Option .1 actually works, with an empty body, while it fails with the actual body: struct A { int[] data; this(ref return scope inout A rhs) inout { data = rhs.data.dup; } } Error: cannot implicitly convert expression `dup(cast(const(int)[])rhs.data)` of type `int[]` to `inout(int[])` It seems that there is no easy way to transition from a postblit to a copy constructor, no?
Aug 12 2021
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 12/08/2021 9:36 PM, Learner wrote:
 It seems that there is no easy way to transition from a postblit to a 
 copy constructor, no?
struct Foo { this(ref Foo other) { foreach(i, v; other.tupleof) this.tupleof[i] = v; } disable this(this); }
Aug 12 2021
parent reply Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 10:10:17 UTC, rikki cattermole 
wrote:
 On 12/08/2021 9:36 PM, Learner wrote:
 It seems that there is no easy way to transition from a 
 postblit to a copy constructor, no?
struct Foo { this(ref Foo other) { foreach(i, v; other.tupleof) this.tupleof[i] = v; } disable this(this); }
This results to: Generating an `inout` copy constructor for `struct A` failed, therefore instances of it are uncopyable
Aug 12 2021
parent reply Tejas <notrealemail gmail.com> writes:
On Thursday, 12 August 2021 at 11:54:22 UTC, Learner wrote:
 On Thursday, 12 August 2021 at 10:10:17 UTC, rikki cattermole 
 wrote:
 On 12/08/2021 9:36 PM, Learner wrote:
 It seems that there is no easy way to transition from a 
 postblit to a copy constructor, no?
struct Foo { this(ref Foo other) { foreach(i, v; other.tupleof) this.tupleof[i] = v; } disable this(this); }
This results to: Generating an `inout` copy constructor for `struct A` failed, therefore instances of it are uncopyable
Just add ```inout``` inside ```this(ref inout/*notice the inout*/ Foo other) inout/*notice the inout*/``` Example code: ```d struct Foo { this(ref inout Foo other) inout { foreach(i, v; other.tupleof) this.tupleof[i] = v; } disable this(this); int a; float b; double c; } void main(){ immutable Foo a; const Foo c; Foo b = a;//mutable b and immutable a const Foo d = c;//const d and const c Foo e = c;//mutable e and const c immutable Foo f = b;//immutable f and mutable b const Foo g = b;//const g and mutable b } ```
Aug 12 2021
parent reply Tejas <notrealemail gmail.com> writes:
On Thursday, 12 August 2021 at 12:19:56 UTC, Tejas wrote:
 On Thursday, 12 August 2021 at 11:54:22 UTC, Learner wrote:
 [...]
Just add ```inout``` inside ```this(ref inout/*notice the inout*/ Foo other) inout/*notice the inout*/``` Example code: ```d struct Foo { this(ref inout Foo other) inout { foreach(i, v; other.tupleof) this.tupleof[i] = v; } disable this(this); int a; float b; double c; } void main(){ immutable Foo a; const Foo c; Foo b = a;//mutable b and immutable a const Foo d = c;//const d and const c Foo e = c;//mutable e and const c immutable Foo f = b;//immutable f and mutable b const Foo g = b;//const g and mutable b } ```
Works with ``` safe``` as well Paul was just trying to make that other answer work, you don't have to make copy constructors ``` trusted```
Aug 12 2021
next sibling parent reply Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 12:22:22 UTC, Tejas wrote:
 On Thursday, 12 August 2021 at 12:19:56 UTC, Tejas wrote:
 [...]
Works with ``` safe``` as well Paul was just trying to make that other answer work, you don't have to make copy constructors ``` trusted```
Operations are needed on `other` data, that was the reason for a `postblit` in the original case: an `int[]` data array needs to be duplicated.
Aug 12 2021
parent Tejas <notrealemail gmail.com> writes:
On Thursday, 12 August 2021 at 12:28:32 UTC, Learner wrote:
 On Thursday, 12 August 2021 at 12:22:22 UTC, Tejas wrote:
 On Thursday, 12 August 2021 at 12:19:56 UTC, Tejas wrote:
 [...]
Works with ``` safe``` as well Paul was just trying to make that other answer work, you don't have to make copy constructors ``` trusted```
Operations are needed on `other` data, that was the reason for a `postblit` in the original case: an `int[]` data array needs to be duplicated.
Hey, this should be good enough now: ```d import std; struct Foo { this(ref inout Foo other) /*inout*/ safe{ /*foreach(i, v; other.tupleof) this.tupleof[i] = cast(typeof(this.tupleof[i]))v;*/ a = other.a; b = other.b; foreach(i, elem ;other.c) c[i] = elem; } disable this(this); int a; float b; double[] c; } void main() safe{ immutable Foo a; const Foo c; Foo b = a;//mutable b from immutable a //writeln(typeof(b).stringof); //Output is Foo const Foo d = c;//const d from const c Foo e = c;//mutable e from const c //immutable Foo f = b;//immutable f from mutable b I don't know why this fails but const from mutable succeeds const Foo g = b;//const g from mutable b } ```
Aug 12 2021
prev sibling parent Tejas <notrealemail gmail.com> writes:
On Thursday, 12 August 2021 at 12:22:22 UTC, Tejas wrote:
 On Thursday, 12 August 2021 at 12:19:56 UTC, Tejas wrote:
 [...]
Works with ``` safe``` as well Paul was just trying to make that other answer work, you don't have to make copy constructors ``` trusted```
Ignore this, it doesn't work for dynamic arrays(but it does for static, ie, fixed length arrays)
Aug 12 2021
prev sibling parent reply drug <drug2004 bk.ru> writes:
12.08.2021 12:36, Learner пишет:

  > It seems that there is no easy way to transition from a postblit to a
 copy constructor, no?
 
 
 
You just need both const and mutable copy ctors to replace inout one: ```D struct A { int[] data; this(ref return scope A rhs) { data = rhs.data.dup; } this(ref return scope const A rhs) const { data = rhs.data.dup; } } ``` the mutable copy ctor accepts mutable data and the const copy ctor accepts const and immutable data
Aug 12 2021
next sibling parent reply drug <drug2004 bk.ru> writes:
12.08.2021 14:07, drug пишет:
 12.08.2021 12:36, Learner пишет:
 
  > It seems that there is no easy way to transition from a postblit to a
 copy constructor, no?
You just need both const and mutable copy ctors to replace inout one: ```D struct A {     int[] data;     this(ref return scope A rhs) { data = rhs.data.dup; }     this(ref return scope const A rhs) const { data = rhs.data.dup; } } ``` the mutable copy ctor accepts mutable data and the const copy ctor accepts const and immutable data
but using inout ctor is easier: ```D struct A { int[] data; this(ref return scope inout A rhs) /* no inout here */ { data = rhs.data.dup; } } ``` The problem is that if you qualify the ctor itself then if you pass const/immutable rhs to it then the ctor is const/immutable too (like the args) and of course you cannot modify this, so the error. To make a copy ctor you need to qualify copy ctor args as inout but the copy ctor itself shall be mutable and have no const,immutable or inout qualifier.
Aug 12 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 12 August 2021 at 11:19:34 UTC, drug wrote:
 ```D
 struct A {
     int[] data;
     this(ref return scope inout A rhs) /* no inout here */ { 
 data = rhs.data.dup; }
 }
 ```
 The problem is that if you qualify the ctor itself then if you 
 pass const/immutable rhs to it then the ctor is const/immutable 
 too (like the args) and of course you cannot modify this, so 
 the error.

 To make a copy ctor you need to qualify copy ctor args as inout 
 but the copy ctor itself shall be mutable and have no 
 const,immutable or inout qualifier.
This is not true. Qualifying the ctor as `inout` works fine: https://run.dlang.io/is/Kpzp5M The problem in this example is that `.dup` always returns a mutable array, even if the array being copied is `inout`. The solution is to cast the copy back to the original type: ```d this(ref return scope inout A rhs) inout { data = cast(typeof(rhs.data)) rhs.data.dup; } ```
Aug 12 2021
next sibling parent drug <drug2004 bk.ru> writes:
12.08.2021 14:32, Paul Backus пишет:
 
 This is not true. Qualifying the ctor as `inout` works fine: 
 https://run.dlang.io/is/Kpzp5M
 
 The problem in this example is that `.dup` always returns a mutable 
 array, even if the array being copied is `inout`. The solution is to 
 cast the copy back to the original type:
 
 ```d
 this(ref return scope inout A rhs) inout
 {
      data = cast(typeof(rhs.data)) rhs.data.dup;
 }
 ```
Yes, it's not true. I forgot that ctors are special in contrast to regular methods and can modify the aggregate they belong to even if the ctor has const/immutable/inout qualifier.
Aug 12 2021
prev sibling next sibling parent reply Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 11:32:03 UTC, Paul Backus wrote:
 On Thursday, 12 August 2021 at 11:19:34 UTC, drug wrote:
 ```D
 struct A {
     int[] data;
     this(ref return scope inout A rhs) /* no inout here */ { 
 data = rhs.data.dup; }
 }
 ```
 The problem is that if you qualify the ctor itself then if you 
 pass const/immutable rhs to it then the ctor is 
 const/immutable too (like the args) and of course you cannot 
 modify this, so the error.

 To make a copy ctor you need to qualify copy ctor args as 
 inout but the copy ctor itself shall be mutable and have no 
 const,immutable or inout qualifier.
This is not true. Qualifying the ctor as `inout` works fine: https://run.dlang.io/is/Kpzp5M The problem in this example is that `.dup` always returns a mutable array, even if the array being copied is `inout`. The solution is to cast the copy back to the original type: ```d this(ref return scope inout A rhs) inout { data = cast(typeof(rhs.data)) rhs.data.dup; } ```
That worked fine, but the codebase is safe: ```d cast from `int[]` to `inout(int[])` not allowed in safe code ``` So copy constructors force me to introduce trusted methods, while that was not necessary with postblits?
Aug 12 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 12 August 2021 at 12:10:49 UTC, Learner wrote:
 That worked fine, but the codebase is  safe:

 ```d
 cast from `int[]` to `inout(int[])` not allowed in safe code
 ```

 So copy constructors force me to introduce trusted methods, 
 while that was not necessary with postblits?
A postblit would simply ignore the type qualifier--which can lead to undefined behavior. (Scroll down to the paragraph that begins "An unqualified postblit..." under ["Struct Postblits"][1] in the spec.) The copy constructor merely forces you to be honest about the safety of your code. In your case, I would recommend encapsulating the unsafe cast in a function like the following: ```d T[] dupWithQualifiers(T[] array) { auto copy = array.dup; return (() trusted => cast(T[]) copy)(); } ``` You can then use this function in place of `dup` in your copy constructor. [1]: https://dlang.org/spec/struct.html#struct-postblit
Aug 12 2021
parent reply Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 13:56:17 UTC, Paul Backus wrote:
 On Thursday, 12 August 2021 at 12:10:49 UTC, Learner wrote:
 That worked fine, but the codebase is  safe:

 ```d
 cast from `int[]` to `inout(int[])` not allowed in safe code
 ```

 So copy constructors force me to introduce trusted methods, 
 while that was not necessary with postblits?
A postblit would simply ignore the type qualifier--which can lead to undefined behavior. (Scroll down to the paragraph that begins "An unqualified postblit..." under ["Struct Postblits"][1] in the spec.) The copy constructor merely forces you to be honest about the safety of your code. In your case, I would recommend encapsulating the unsafe cast in a function like the following: ```d T[] dupWithQualifiers(T[] array) { auto copy = array.dup; return (() trusted => cast(T[]) copy)(); } ``` You can then use this function in place of `dup` in your copy constructor. [1]: https://dlang.org/spec/struct.html#struct-postblit
Thank you, now everything is more clear. A last question, if you do not mind, just to better understand inout. It seems a shortcut to avoid repeating the same function body for mutable, const, and immutable. Why the following code is not equal to the single inout constructor? struct A { int[] data; //this(ref return scope inout A rhs) inout { /*body*/ } this(ref return scope Timestamp rhs) { /*body*/ } this(ref return scope const Timestamp rhs) const { /*body*/ } this(ref return scope immutable Timestamp rhs) immutable { /*body*/ } } Error: Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable Inout is compatible only with inout, and not with the unrolled code it implies?
Aug 12 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/12/21 10:08 AM, Learner wrote:
 On Thursday, 12 August 2021 at 13:56:17 UTC, Paul Backus wrote:
 On Thursday, 12 August 2021 at 12:10:49 UTC, Learner wrote:
 That worked fine, but the codebase is  safe:

 ```d
 cast from `int[]` to `inout(int[])` not allowed in safe code
 ```

 So copy constructors force me to introduce trusted methods, while 
 that was not necessary with postblits?
A postblit would simply ignore the type qualifier--which can lead to undefined behavior. (Scroll down to the paragraph that begins "An unqualified postblit..." under ["Struct Postblits"][1] in the spec.) The copy constructor merely forces you to be honest about the safety of your code. In your case, I would recommend encapsulating the unsafe cast in a function like the following: ```d T[] dupWithQualifiers(T[] array) {     auto copy = array.dup;     return (() trusted => cast(T[]) copy)(); } ``` You can then use this function in place of `dup` in your copy constructor. [1]: https://dlang.org/spec/struct.html#struct-postblit
Thank you, now everything is more clear. A last question, if you do not mind, just to better understand inout. It seems a shortcut to avoid repeating the same function body for mutable, const, and immutable. Why the following code is not equal to the single inout constructor?     struct A {         int[] data;         //this(ref return scope inout A rhs) inout { /*body*/ }         this(ref return scope           Timestamp rhs) { /*body*/ }         this(ref return scope const     Timestamp rhs) const { /*body*/ }         this(ref return scope immutable Timestamp rhs) immutable { /*body*/ }     }     Error: Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable Inout is compatible only with inout, and not with the unrolled code it implies?
inout is not like a template. It's a separate qualifier that generates only one function (not 3 unrolled ones). It's sort of viral like const is viral -- all underlying pieces have to support inout in order for you to write inout functions. -Steve
Aug 12 2021
parent reply Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 14:57:16 UTC, Steven Schveighoffer 
wrote:
 On 8/12/21 10:08 AM, Learner wrote:
 On Thursday, 12 August 2021 at 13:56:17 UTC, Paul Backus wrote:
 On Thursday, 12 August 2021 at 12:10:49 UTC, Learner wrote:
 That worked fine, but the codebase is  safe:

 ```d
 cast from `int[]` to `inout(int[])` not allowed in safe code
 ```

 So copy constructors force me to introduce trusted methods, 
 while that was not necessary with postblits?
A postblit would simply ignore the type qualifier--which can lead to undefined behavior. (Scroll down to the paragraph that begins "An unqualified postblit..." under ["Struct Postblits"][1] in the spec.) The copy constructor merely forces you to be honest about the safety of your code. In your case, I would recommend encapsulating the unsafe cast in a function like the following: ```d T[] dupWithQualifiers(T[] array) {     auto copy = array.dup;     return (() trusted => cast(T[]) copy)(); } ``` You can then use this function in place of `dup` in your copy constructor. [1]: https://dlang.org/spec/struct.html#struct-postblit
Thank you, now everything is more clear. A last question, if you do not mind, just to better understand inout. It seems a shortcut to avoid repeating the same function body for mutable, const, and immutable. Why the following code is not equal to the single inout constructor?     struct A {         int[] data;         //this(ref return scope inout A rhs) inout { /*body*/ }         this(ref return scope           Timestamp rhs) { /*body*/ }         this(ref return scope const     Timestamp rhs) const { /*body*/ }         this(ref return scope immutable Timestamp rhs) immutable { /*body*/ }     }     Error: Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable Inout is compatible only with inout, and not with the unrolled code it implies?
inout is not like a template. It's a separate qualifier that generates only one function (not 3 unrolled ones). It's sort of viral like const is viral -- all underlying pieces have to support inout in order for you to write inout functions. -Steve
It is not clear to me why the inout generated copy constructor of the B structure is not able to copy the A structure. struct A { int[] data; this(ref return scope A rhs) { /* body */ } this(ref return scope const A rhs) const { /* body */} this(ref return scope immutable A rhs) immutable { /* body */} } struct B { // default generated copy constructor, by section 14.15.6.2 this(ref return scope inout(B) src) inout { foreach (i, ref inout field; src.tupleof) this.tupleof[i] = field; } } Can point me to a code example of when the D generated copy constructor fails to copy A, and why?
Aug 12 2021
next sibling parent reply Tejas <notrealemail gmail.com> writes:
On Thursday, 12 August 2021 at 15:39:40 UTC, Learner wrote:
 On Thursday, 12 August 2021 at 14:57:16 UTC, Steven 
 Schveighoffer wrote:
 [...]
It is not clear to me why the inout generated copy constructor of the B structure is not able to copy the A structure. [...]
Why will copy constructor of ```struct B``` accept argument of type ```A```?
Aug 12 2021
parent Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 15:50:05 UTC, Tejas wrote:
 On Thursday, 12 August 2021 at 15:39:40 UTC, Learner wrote:
 On Thursday, 12 August 2021 at 14:57:16 UTC, Steven 
 Schveighoffer wrote:
 [...]
It is not clear to me why the inout generated copy constructor of the B structure is not able to copy the A structure. [...]
Why will copy constructor of ```struct B``` accept argument of type ```A```?
You are right, I forgot the A member, now it is clear: struct A { int[] data; this(ref return scope A rhs) {} this(ref return scope const A rhs) const {} this(ref return scope immutable A rhs) immutable {} } struct B { // default generated copy constructor, by section 14.15.6.2 this(ref return scope inout(B) src) inout { foreach (i, ref inout field; src.tupleof) this.tupleof[i] = field; } A a; } Error: none of the overloads of `__ctor` are callable using a `inout` object, candidates are: `A.this(return ref scope A rhs)` `A.this(return ref scope const(A) rhs)` `A.this(return ref scope immutable(A) rhs)` Thank you everybody.
Aug 12 2021
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 12 August 2021 at 15:39:40 UTC, Learner wrote:
 It is not clear to me why the inout generated copy constructor 
 of the B structure is not able to copy the A structure.

     struct A
     {
         int[] data;

         this(ref return scope           A rhs)            { /* 
 body */ }
         this(ref return scope const     A rhs) const      { /* 
 body */}
         this(ref return scope immutable A rhs) immutable  { /* 
 body */}
     }

     struct B
     {
         // default generated copy constructor, by section 
 14.15.6.2
         this(ref return scope inout(B) src) inout
         {
             foreach (i, ref inout field; src.tupleof) 
 this.tupleof[i] = field;
         }
     }

 Can point me to a code example of when the D generated copy 
 constructor fails to copy A, and why?
You have forgotten to add a member variable of type `A` to your `B` struct. If you add one, you will see the following error message: ``` onlineapp.d(18): Error: none of the overloads of `__ctor` are callable using a `inout` object, candidates are: onlineapp.d(5): `onlineapp.A.this(return ref scope A rhs)` onlineapp.d(6): `onlineapp.A.this(return ref scope const(A) rhs)` onlineapp.d(7): `onlineapp.A.this(return ref scope immutable(A) rhs)` ``` (Full example: https://run.dlang.io/is/9BrpZj) Essentially, you cannot use a mutable, `const`, or `immutable` copy constructor to copy an `inout` object, only an `inout` copy constructor. The reason for this is a bit subtle. Normally, `inout` can convert to `const`, so you might expect that the `const` copy constructor could be used to construct a copy of an `inout` object. However, copy constructors take their arguments by `ref`, and implicit conversions are not allowed for arguments passed to `ref` parameters. (I cannot find a citation in the spec for this, but you can verify it yourself.) Here's a simplified example that gives the same error: ```d void f(inout(int)[] a) { g(a); } void g(ref int[] a) {} void g(ref const(int)[] a) {} void g(ref immutable(int)[] a) {} ```
Aug 12 2021
next sibling parent Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 16:12:39 UTC, Paul Backus wrote:
 On Thursday, 12 August 2021 at 15:39:40 UTC, Learner wrote:
 [...]
You have forgotten to add a member variable of type `A` to your `B` struct. If you add one, you will see the following error message: [...]
"implicit conversions are not allowed for arguments passed to `ref` parameters" was the point missing to me. Good to know, and thank yo to everyone!
Aug 12 2021
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/12/21 12:12 PM, Paul Backus wrote:
 The reason for this is a bit subtle. Normally, `inout` can convert to 
 `const`, so you might expect that the `const` copy constructor could be 
 used to construct a copy of an `inout` object. However, copy 
 constructors take their arguments by `ref`, and implicit conversions are 
 not allowed for arguments passed to `ref` parameters. (I cannot find a 
 citation in the spec for this, but you can verify it yourself.)
implicit const conversions are possible via a single ref, but not through a double reference. In this case, it's not the passing of the inout object to the const constructor. The issue is that you can't convert const (or immutable or mutable) to inout implicitly, and the member variable is inout inside an inout constructor. Therefore, there's no viable copy constructor to call for the member, and the outer copy constructor cannot be generated. As a side note, inout actually *can* bind to double references of any mutability, unlike const, which is a nice feature that is seldom talked about. -Steve
Aug 13 2021
parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 13 August 2021 at 15:26:15 UTC, Steven Schveighoffer 
wrote:
 The issue is that you can't convert const (or immutable or 
 mutable) to inout implicitly, and the member variable is inout 
 inside an inout constructor. Therefore, there's no viable copy 
 constructor to call for the member, and the outer copy 
 constructor cannot be generated.
I'm not quite sure I follow this. Are you saying that the constructor call is typechecked as if it were written like this: ```d this.field = this.field.__ctor(rhs.field); ``` ...and not like this? ```d this.field.__ctor(rhs.field); ``` Because I can see how the first version would involve an const-to-inout conversion, but the second version looks like it ought to work.
Aug 13 2021
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/13/21 4:58 PM, Paul Backus wrote:
 On Friday, 13 August 2021 at 15:26:15 UTC, Steven Schveighoffer wrote:
 The issue is that you can't convert const (or immutable or mutable) to 
 inout implicitly, and the member variable is inout inside an inout 
 constructor. Therefore, there's no viable copy constructor to call for 
 the member, and the outer copy constructor cannot be generated.
I'm not quite sure I follow this. Are you saying that the constructor call is typechecked as if it were written like this: ```d this.field = this.field.__ctor(rhs.field); ``` ...and not like this? ```d this.field.__ctor(rhs.field); ``` Because I can see how the first version would involve an const-to-inout conversion, but the second version looks like it ought to work.
My point was just that `ref inout` normally binds to `ref const`. Example: ```d void foo(ref const int a) { writeln("hi, a is ", a); } void bar(ref inout int b) { foo(b); // fine } ``` You implied in your statement that it was the ref-ness that prevents the call. Your simplified example was also a bit off, it was a double indirection of a ref array (which is definitely forbidden to be implicit cast). But for constructors it's not the same. Essentially because constructors have different rules for what they can do with their inputs (the inout `this` parameter can be assigned to for the member's first assignment). What I was trying to say (poorly) is that inside the inout copy ctor, you can actually call the const `A` copy constructor with an input of the other `inout A`. You just can't call it on the member (or assign it to the member), because that would allow some bad things to happen in some cases. -Steve
Aug 13 2021
parent Paul Backus <snarwin gmail.com> writes:
On Friday, 13 August 2021 at 21:34:29 UTC, Steven Schveighoffer 
wrote:
 But for constructors it's not the same. Essentially because 
 constructors have different rules for what they can do with 
 their inputs (the inout `this` parameter can be assigned to for 
 the member's first assignment).

 What I was trying to say (poorly) is that inside the inout copy 
 ctor, you can actually call the const `A` copy constructor with 
 an input of the other `inout A`. You just can't call it on the 
 member (or assign it to the member), because that would allow 
 some bad things to happen in some cases.
Thanks; the special treatment of `this` is the part I was missing. As far as I can tell there is no mention in the language spec of how this works. Probably worth documenting.
Aug 13 2021
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 8/12/21 4:32 AM, Paul Backus wrote:

 Qualifying the ctor as `inout` works fine
I can see how a DConf Online presention is shaping up in your head. ;) http://dconf.org/2021/online/index.html We need a collective understanding of effective use of such fundamental concepts. Ali
Aug 12 2021
prev sibling parent Learner <learner gmail.com> writes:
On Thursday, 12 August 2021 at 11:07:24 UTC, drug wrote:
 12.08.2021 12:36, Learner пишет:

  > It seems that there is no easy way to transition from a 
 postblit to a
 copy constructor, no?
 
 
 
You just need both const and mutable copy ctors to replace inout one: ```D struct A { int[] data; this(ref return scope A rhs) { data = rhs.data.dup; } this(ref return scope const A rhs) const { data = rhs.data.dup; } } ``` the mutable copy ctor accepts mutable data and the const copy ctor accepts const and immutable data
That still fails: Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable Also if I remove the `const` body (can I assign data if the method is const?) ```D struct A { int[] data; this(ref return scope A rhs) { data = rhs.data.dup; } this(ref return scope const A rhs) const {} } Generating an `inout` copy constructor for `struct B` failed, therefore instances of it are uncopyable ```
Aug 12 2021