Thread overview
Problem with insertBack
Jun 03, 2016
John Nixon
Jun 03, 2016
ag0aep6g
Jun 03, 2016
John Nixon
June 03, 2016
I recently came across another problem with my program in D, found a minimal program showing it, and experimented a little with it as follows:

import std.stdio;
import std.container;
struct CS{
  char[] t;
  CS dup()const{
    CS cs;
    cs.t = this.t.dup;
    return cs;}
};
void main(){
  Array!CS cs_array = make!(Array!CS)();
  CS cs;
  cs.t = "bb".dup;
  cs_array.insertBack(cs);
  write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
  cs.t[0] = ("a".dup)[0];//this changes cs_array!
  write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
  cs.t = "c".dup;//but this does not
  write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
  return;}

The first assignment to cs.t[0] changes cs_array as indicated, but the second to cs.t does not. Also if these two assignments are reversed, neither affects cs_array. What could be the explanation of this?

Kind regards

John Nixon

June 03, 2016
On 6/3/16 10:34 AM, John Nixon wrote:
> I recently came across another problem with my program in D, found a
> minimal program showing it, and experimented a little with it as follows:
>
> import std.stdio;
> import std.container;
> struct CS{
>   char[] t;
>   CS dup()const{
>     CS cs;
>     cs.t = this.t.dup;
>     return cs;}
> };
> void main(){
>   Array!CS cs_array = make!(Array!CS)();
>   CS cs;
>   cs.t = "bb".dup;
>   cs_array.insertBack(cs);
>   write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>   cs.t[0] = ("a".dup)[0];//this changes cs_array!

You have inside your CS struct a pointer to a heap array. Then you change the heap array later. The CS element you put into the cs_array still points at the same piece of memory.

Perhaps you meant to insert cs.dup?

>   write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>   cs.t = "c".dup;//but this does not

Right, because the cs stored on the stack is now pointing at a different heap-allocated array

>   write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>   return;}
>
> The first assignment to cs.t[0] changes cs_array as indicated, but the
> second to cs.t does not. Also if these two assignments are reversed,
> neither affects cs_array. What could be the explanation of this?

Right, because if the stack is pointing at a different array than the cs_array[0], then altering won't affect cs_array[0].

-Steve
June 03, 2016
On 06/03/2016 04:34 PM, John Nixon wrote:
> import std.stdio;
> import std.container;
> struct CS{
>    char[] t;
>    CS dup()const{
>      CS cs;
>      cs.t = this.t.dup;
>      return cs;}
> };

Aside: No semicolon after struct declarations in D.

> void main(){
>    Array!CS cs_array = make!(Array!CS)();

cs_array stores its data somewhere on the heap.

>    CS cs;

cs is on the stack.

>    cs.t = "bb".dup;

But cs.t's data is on the heap, like cs_array's. cs.t contains a pointer to it.

>    cs_array.insertBack(cs);

This copies cs to cs_array's heap. The pointer in cs.t is being copied, but the data that it points to is not being copied. So cs.t and cs_array[0].t contain two distinct but equal pointers now. Since they're equal, they refer to the same data.

>    write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>    cs.t[0] = ("a".dup)[0];//this changes cs_array!

This is expected since cs.t and cs_array[0].t point to the same location. A change to the data through one of them is visible to the other.

>    write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>    cs.t = "c".dup;//but this does not

Here you're not writing through the pointer in cs.t, but rather you're assigning a whole new one. Since the pointers in cs.t and cs_array[0].t are independent of each other, they now point to different locations, with different data.

>    write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>    return;}
June 03, 2016
On Friday, 3 June 2016 at 15:03:45 UTC, ag0aep6g wrote:
> On 06/03/2016 04:34 PM, John Nixon wrote:
>> import std.stdio;
>> import std.container;
>> struct CS{
>>    char[] t;
>>    CS dup()const{
>>      CS cs;
>>      cs.t = this.t.dup;
>>      return cs;}
>> };
>
> Aside: No semicolon after struct declarations in D.
>
>> void main(){
>>    Array!CS cs_array = make!(Array!CS)();
>
> cs_array stores its data somewhere on the heap.
>
>>    CS cs;
>
> cs is on the stack.
>
>>    cs.t = "bb".dup;
>
> But cs.t's data is on the heap, like cs_array's. cs.t contains a pointer to it.
>
>>    cs_array.insertBack(cs);
>
> This copies cs to cs_array's heap. The pointer in cs.t is being copied, but the data that it points to is not being copied. So cs.t and cs_array[0].t contain two distinct but equal pointers now. Since they're equal, they refer to the same data.
>
>>    write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>>    cs.t[0] = ("a".dup)[0];//this changes cs_array!
>
> This is expected since cs.t and cs_array[0].t point to the same location. A change to the data through one of them is visible to the other.
>
>>    write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>>    cs.t = "c".dup;//but this does not
>
> Here you're not writing through the pointer in cs.t, but rather you're assigning a whole new one. Since the pointers in cs.t and cs_array[0].t are independent of each other, they now point to different locations, with different data.
>
>>    write("cs_array = ");foreach(i2;cs_array)write(i2);writeln("");
>>    return;}

On Friday, 3 June 2016 at 15:03:45 UTC, ag0aep6g wrote:

Thanks very much to you and Steve for the detailed explanation. By the way I did try insertBack(cs.dup) but wasn't sure what I was doing. It seems that I just need to add all the .dup 's when needed to make my program work and find out a procedure to make sure I don't miss any.

John Nixon