August 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:

>
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:

this(ref return scope inout A rhs) inout
{
    data = cast(typeof(rhs.data)) rhs.data.dup;
}

That worked fine, but the codebase is @safe:

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?

August 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:

>

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:

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
}

August 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:

>

[...]

Just add inout inside this(ref inout/*notice the inout*/ Foo other) inout/*notice the inout*/

Example code:

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

August 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:

>

[...]

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.

August 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:

>

[...]

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)

August 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:

>

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:

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
}
August 12, 2021

On Thursday, 12 August 2021 at 12:10:49 UTC, Learner wrote:

>

That worked fine, but the codebase is @safe:

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" 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:

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.

August 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:

>

That worked fine, but the codebase is @safe:

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" 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:

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.

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?

August 12, 2021

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:

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" 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:

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.

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

August 12, 2021

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:

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" 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:

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.

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?