Thread overview
Can a struct be copied by its const method?
Aug 02, 2013
Ivan Kazmenko
Aug 02, 2013
monarch_dodra
Aug 02, 2013
Ivan Kazmenko
August 02, 2013
The question is as stated: can a struct be copied by its const method?  I'd expect the answer to be the same for any struct, but the following two examples say different.  I'm using DMD 2.063.2 on Windows.

Example 1. Struct containing an int.  Postblit added to resemble Example 2 closely.
-----
int f (int val) {return val;}
struct Struct
{
	int val;
	this (this) {val = f (val);}
	void make_copy () const {dest = this;}
}
Struct dest;
void main () { }
-----

Example 2. Struct containing a dynamic array.
-----
struct Struct
{
	int [] arr;
	this (this) {arr = arr.dup;}
	void make_copy () const {dest = this;}
}
Struct dest;
void main () { }
-----

Example 1 compiles fine, but Example 2 gives an error:
arrs.d(12): Error: function arrs.Struct.opAssign (Struct p) is not callable using argument types (const(Struct))

Is that forbidden on purpose?  If so, why?  Removing the postblit does not help (and breaks the intent to make a full copy).

Workaround 1 is to make a cast:
	void make_copy () const {dest = cast (Struct) this;}

Workaround 2 is to remove const-ness:
	void make_copy () {dest = this;}

In my larger program, Workaround 1 leads to errors, while Workaround 2 works as intended.  However, I don't yet have a minimal example of that, so that may be my own bug.  Anyway, am I doing the cast right?

Ivan Kazmenko.
August 02, 2013
On Friday, 2 August 2013 at 15:06:34 UTC, Ivan Kazmenko wrote:
> The question is as stated: can a struct be copied by its const method?  I'd expect the answer to be the same for any struct, but the following two examples say different.  I'm using DMD 2.063.2 on Windows.
>
> Example 1. Struct containing an int.  Postblit added to resemble Example 2 closely.
> -----
> int f (int val) {return val;}
> struct Struct
> {
> 	int val;
> 	this (this) {val = f (val);}
> 	void make_copy () const {dest = this;}
> }
> Struct dest;
> void main () { }
> -----
>
> Example 2. Struct containing a dynamic array.
> -----
> struct Struct
> {
> 	int [] arr;
> 	this (this) {arr = arr.dup;}
> 	void make_copy () const {dest = this;}
> }
> Struct dest;
> void main () { }
> -----
>
> Example 1 compiles fine, but Example 2 gives an error:
> arrs.d(12): Error: function arrs.Struct.opAssign (Struct p) is not callable using argument types (const(Struct))
>
> Is that forbidden on purpose?  If so, why?  Removing the postblit does not help (and breaks the intent to make a full copy).
>
> Workaround 1 is to make a cast:
> 	void make_copy () const {dest = cast (Struct) this;}
>
> Workaround 2 is to remove const-ness:
> 	void make_copy () {dest = this;}
>
> In my larger program, Workaround 1 leads to errors, while Workaround 2 works as intended.  However, I don't yet have a minimal example of that, so that may be my own bug.  Anyway, am I doing the cast right?
>
> Ivan Kazmenko.

To answer real quick, I think the problem is when your type has aliasing, then dmd has no way of ensuring that after the postblit, your new struct won't alias any data in the old struct. Because of this, it is simply not able to generate an "opAssign(const struct rhs)", and only provides "opAssign(Struct rhs)".

If you add these implementation yourself:

  ref Struct opAssign(const ref Struct rhs)
  {
    //Pass by ref. make a dup
    arr = rhs.arr.dup;
    return this;
  }
  ref Struct opAssign(const Struct rhs)
  {
    //pass by value
    //postblit has already been dup'ed, so we can just force alias
    arr = cast(int[])rhs.arr;
    return this;
  }

The problem seems to get fixed.

As a side note, implementing these yourself tends to be faster then relying on a postblit implemented opAssign.
August 02, 2013
On Friday, 2 August 2013 at 15:47:49 UTC, monarch_dodra wrote:
> On Friday, 2 August 2013 at 15:06:34 UTC, Ivan Kazmenko wrote:
>> The question is as stated: can a struct be copied by its const method?  I'd expect the answer to be the same for any struct, but the following two examples say different.  I'm using DMD 2.063.2 on Windows.
>> <...>
>
> To answer real quick, I think the problem is when your type has aliasing, then dmd has no way of ensuring that after the postblit, your new struct won't alias any data in the old struct. Because of this, it is simply not able to generate an "opAssign(const struct rhs)", and only provides "opAssign(Struct rhs)".
>
> If you add these implementation yourself:
>
>   ref Struct opAssign(const ref Struct rhs)
>   {
>     //Pass by ref. make a dup
>     arr = rhs.arr.dup;
>     return this;
>   }
>   ref Struct opAssign(const Struct rhs)
>   {
>     //pass by value
>     //postblit has already been dup'ed, so we can just force alias
>     arr = cast(int[])rhs.arr;
>     return this;
>   }
>
> The problem seems to get fixed.
>
> As a side note, implementing these yourself tends to be faster then relying on a postblit implemented opAssign.

Thank you!  After adding the appropriate opAssign-s, everything seems to work as intended.