www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - full copies on assignment

reply John Nixon <john.h.nixon1 gmail.com> writes:
  1 import std.stdio;
   2
   3 struct CS{
   4   char[] t;
   5   CS opAssign(const CS rhs){
   6     writeln("CS.opAssign called");
   7     this.t = rhs.t.dup;
   8     return this;}
   9 };
  10 void test_fun(const ref CS rhs){
  11   CS cs = rhs;//error cannot implicitly convert expression 
(rhs) of type const(CS) to CS
  12   CS cs;// these two lines in place of the line above
  13   cs = rhs;//now give a program that runs correctly but needs 
CS.opAssign
  14   writeln("cs = ",cs);}
  15
  16 void main(){
  17   CS rhs;
  18   rhs.t="string".dup;
  19   test_fun(rhs);
  20   return;}

In the above program I noticed the impossibility of compiling a 
declaration of a struct and its assignment in the same line (line 
11) inside a function which has, as a parameter, a const ref to 
the RHS of the assignment. This naively doesn’t seem right 
because the RHS of an assignment should not be altered by it. On 
investigating further, I could compile the program (with DMD64 D 
Compiler v2.071.0) by adding CS.opAssign as indicated and 
splitting line 11 into lines 12 and 13 as the comments suggest. 
This has do to with D not making a full (deep) copy on assignment 
of a CS because it contains a char[], instead giving a pointer to 
it that could be used to change it. I think full copies should be 
made on assignment regardless of type.

It seems to me that shallow copying of objects is partially in 
conflict with the const system, and full copying should be the 
default in assignment (perhaps over-ridable by using a keyword eg 
alias) which would simplify the const system not giving access to 
pointers, and would allow the above program to compile in the way 
I first thought of (without CS.opAssign and lines 12 and 13).

I think this is almost the same issue as discussed in “Copying 
structs with pointers” on 2011-07-20
May 24 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 24 May 2016 at 14:29:53 UTC, John Nixon wrote:
 This naively doesn’t seem right because the RHS of an 
 assignment should not be altered by it.
It's because the char[] being shallow copied still leads to mutable stuff. What I typically do here is just add a method `dup` to the struct that deep copies. Then you do `CS cs = rhs.dup;` when you want to construct it, like you'd do with a copy of a naked array.
May 24 2016
parent reply John Nixon <john.h.nixon1 gmail.com> writes:
On Tuesday, 24 May 2016 at 15:17:37 UTC, Adam D. Ruppe wrote:
 On Tuesday, 24 May 2016 at 14:29:53 UTC, John Nixon wrote:
 This naively doesn’t seem right because the RHS of an 
 assignment should not be altered by it.
It's because the char[] being shallow copied still leads to mutable stuff. What I typically do here is just add a method `dup` to the struct that deep copies. Then you do `CS cs = rhs.dup;` when you want to construct it, like you'd do with a copy of a naked array.
Thank you for this suggestion, perhaps a slightly neater workaround.
May 24 2016
parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Tuesday, 24 May 2016 at 20:58:11 UTC, John Nixon wrote:
 On Tuesday, 24 May 2016 at 15:17:37 UTC, Adam D. Ruppe wrote:
 On Tuesday, 24 May 2016 at 14:29:53 UTC, John Nixon wrote:
 This naively doesn’t seem right because the RHS of an 
 assignment should not be altered by it.
It's because the char[] being shallow copied still leads to mutable stuff. What I typically do here is just add a method `dup` to the struct that deep copies. Then you do `CS cs = rhs.dup;` when you want to construct it, like you'd do with a copy of a naked array.
Thank you for this suggestion, perhaps a slightly neater workaround.
Or add an explicit constructor: struct CS { // ... this(const CS rhs) { this = rhs; } } Then you can write: auto cs = CS(rhs);
May 25 2016
parent reply John Nixon <john.h.nixon1 gmail.com> writes:
On Wednesday, 25 May 2016 at 15:44:34 UTC, Marc Schütz wrote:
 On Tuesday, 24 May 2016 at 20:58:11 UTC, John Nixon wrote:
 On Tuesday, 24 May 2016 at 15:17:37 UTC, Adam D. Ruppe wrote:
 On Tuesday, 24 May 2016 at 14:29:53 UTC, John Nixon wrote:
 Or add an explicit constructor:

     struct CS {
         // ...
         this(const CS rhs) { this = rhs; }
Unfortunately this results in "Error: cannot implicitly convert expression (rhs) of type const(CS) to CS'.
May 26 2016
parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Thursday, 26 May 2016 at 10:51:30 UTC, John Nixon wrote:
 On Wednesday, 25 May 2016 at 15:44:34 UTC, Marc Schütz wrote:
 On Tuesday, 24 May 2016 at 20:58:11 UTC, John Nixon wrote:
 On Tuesday, 24 May 2016 at 15:17:37 UTC, Adam D. Ruppe wrote:
 On Tuesday, 24 May 2016 at 14:29:53 UTC, John Nixon wrote:
 Or add an explicit constructor:

     struct CS {
         // ...
         this(const CS rhs) { this = rhs; }
Unfortunately this results in "Error: cannot implicitly convert expression (rhs) of type const(CS) to CS'.
Hmm... this is the full program that works for me: import std.stdio; struct CS { char[] t; this(const CS rhs) { this = rhs; } CS opAssign(const CS rhs) { writeln("CS.opAssign called"); this.t = rhs.t.dup; return this; } }; void test_fun(const ref CS rhs) { auto cs = CS(rhs); writeln("cs = ",cs); } void main() { CS rhs; rhs.t = "string".dup; test_fun(rhs); return; }
May 27 2016
parent John Nixon <john.h.nixon1 gmail.com> writes:
On Friday, 27 May 2016 at 08:59:43 UTC, Marc Schütz wrote:

Yes indeed it does. Thanks. Something in my version must have 
been different.
May 29 2016