digitalmars.D.learn - I do not understand copy constructors
- Learner (13/13) Aug 12 2021 I have a structure like, used by other structures:
- Paul Backus (30/37) Aug 12 2021 When the compiler generates a copy constructor for a struct, it
- Learner (20/59) Aug 12 2021 While option 2. is not working:
- rikki cattermole (8/10) Aug 12 2021 struct Foo {
- Learner (5/15) Aug 12 2021 This results to:
- Tejas (25/43) Aug 12 2021 Just add ```inout``` inside ```this(ref inout/*notice the inout*/
- drug (11/16) Aug 12 2021 You just need both const and mutable copy ctors to replace inout one:
- drug (15/34) Aug 12 2021 but using inout ctor is easier:
- Paul Backus (12/26) Aug 12 2021 This is not true. Qualifying the ctor as `inout` works fine:
- drug (4/18) Aug 12 2021 Yes, it's not true. I forgot that ctors are special in contrast to
- Learner (7/34) Aug 12 2021 That worked fine, but the codebase is @safe:
- Paul Backus (18/24) Aug 12 2021 A postblit would simply ignore the type qualifier--which can lead
- Learner (21/48) Aug 12 2021 Thank you, now everything is more clear.
- Steven Schveighoffer (6/62) Aug 12 2021 inout is not like a template. It's a separate qualifier that generates
- Learner (26/91) Aug 12 2021 It is not clear to me why the inout generated copy constructor of
- Tejas (3/9) Aug 12 2021 Why will copy constructor of ```struct B``` accept argument of
- Learner (26/37) Aug 12 2021 You are right, I forgot the A member, now it is clear:
- Paul Backus (34/58) Aug 12 2021 You have forgotten to add a member variable of type `A` to your
- Learner (4/10) Aug 12 2021 "implicit conversions are not allowed for arguments passed to
- Steven Schveighoffer (12/18) Aug 13 2021 implicit const conversions are possible via a single ref, but not
- Paul Backus (14/19) Aug 13 2021 I'm not quite sure I follow this. Are you saying that the
- Steven Schveighoffer (26/47) Aug 13 2021 My point was just that `ref inout` normally binds to `ref const`.
- Paul Backus (5/14) Aug 13 2021 Thanks; the special treatment of `this` is the part I was missing.
- =?UTF-8?Q?Ali_=c3=87ehreli?= (6/7) Aug 12 2021 I can see how a DConf Online presention is shaping up in your head. ;)
- Learner (15/34) Aug 12 2021 That still fails:
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
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
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: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?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
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
On Thursday, 12 August 2021 at 10:10:17 UTC, rikki cattermole wrote:On 12/08/2021 9:36 PM, Learner wrote:This results to: Generating an `inout` copy constructor for `struct A` failed, therefore instances of it are uncopyableIt 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
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: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 } ```On 12/08/2021 9:36 PM, Learner wrote:This results to: Generating an `inout` copy constructor for `struct A` failed, therefore instances of it are uncopyableIt 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
On Thursday, 12 August 2021 at 12:19:56 UTC, Tejas wrote:On Thursday, 12 August 2021 at 11:54:22 UTC, Learner 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```[...]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
On Thursday, 12 August 2021 at 12:22:22 UTC, Tejas wrote:On Thursday, 12 August 2021 at 12:19:56 UTC, Tejas wrote: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.[...]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
On Thursday, 12 August 2021 at 12:28:32 UTC, Learner wrote:On Thursday, 12 August 2021 at 12:22:22 UTC, Tejas wrote: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 } ```On Thursday, 12 August 2021 at 12:19:56 UTC, Tejas wrote: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.[...]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
On Thursday, 12 August 2021 at 12:22:22 UTC, Tejas wrote:On Thursday, 12 August 2021 at 12:19:56 UTC, Tejas wrote:Ignore this, it doesn't work for dynamic arrays(but it does for static, ie, fixed length arrays)[...]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
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
12.08.2021 14:07, drug пишет:12.08.2021 12:36, Learner пишет: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.> 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
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
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
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: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?```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
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
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: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?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
On 8/12/21 10:08 AM, Learner wrote:On Thursday, 12 August 2021 at 13:56:17 UTC, Paul Backus wrote: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. -SteveOn Thursday, 12 August 2021 at 12:10:49 UTC, Learner wrote: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?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
On Thursday, 12 August 2021 at 14:57:16 UTC, Steven Schveighoffer wrote:On 8/12/21 10:08 AM, 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?On Thursday, 12 August 2021 at 13:56:17 UTC, Paul Backus wrote: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. -SteveOn Thursday, 12 August 2021 at 12:10:49 UTC, Learner wrote: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?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
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:Why will copy constructor of ```struct B``` accept argument of type ```A```?[...]It is not clear to me why the inout generated copy constructor of the B structure is not able to copy the A structure. [...]
Aug 12 2021
On Thursday, 12 August 2021 at 15:50:05 UTC, Tejas wrote:On Thursday, 12 August 2021 at 15:39:40 UTC, Learner wrote: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.On Thursday, 12 August 2021 at 14:57:16 UTC, Steven Schveighoffer wrote:Why will copy constructor of ```struct B``` accept argument of type ```A```?[...]It is not clear to me why the inout generated copy constructor of the B structure is not able to copy the A structure. [...]
Aug 12 2021
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
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:"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![...]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: [...]
Aug 12 2021
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
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
On 8/13/21 4:58 PM, Paul Backus wrote:On Friday, 13 August 2021 at 15:26:15 UTC, Steven Schveighoffer wrote: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. -SteveThe 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
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
On 8/12/21 4:32 AM, Paul Backus wrote:Qualifying the ctor as `inout` works fineI 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
On Thursday, 12 August 2021 at 11:07:24 UTC, drug wrote:12.08.2021 12:36, Learner пишет: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 ```> 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