October 25, 2022
On Wed, Oct 26, 2022 at 12:16:55AM +0000, Salih Dincer via Digitalmars-d-learn wrote: [...]
> I've known D for more than 10 years, but the topic we're talking about still seems strange to me.  The explanations given are not enough for me, I'm sorry.
> 
> Can anyone tell me what happens when I change the location of the structure?  So the X structure must be in the stack when it is in main(), and the following must be in the heap, right?
> 
> ```d
> import std;
> 
> //void main() {
> 
>   struct X
>   {
>     struct Y {
>       int i = 10;
>       alias i this;
>     }
>     Y[] y = [Y.init];

This declares y to be a slice of some memory somewhere (it can be either the stack or the heap), and initializes it to point to a 1-element array containing Y.init.

Here's an important quirk in D: whenever you initialize an aggregate array member with a literal, ALL instances of the aggregate will point to the *same* underlying array (see below).

If you want to avoid the confusing situation below, my recommendation is that you initialize .y inside the ctor instead. Then it will be more explicit what exactly is going on.


>     string toString() {
>       return y.format!"%s";
>     }
>   }
> 
> void main() {
> 
>   X[2] x;

This declares a static array of 2 elements, each of which is an X. Each instance of X contains a member y that is a slice that points to the array [Y.init].  Each instance of X sits on the stack; however, their members y point somewhere else, in this case, to the array [Y.init].

And here's the important point: as mentioned above, [Y.init] here is the SAME ARRAY that's being referred to by two different slices: x[0].y and x[1].y.

If you'll excuse some ASCII art, here's the situation you have:

	STACK                  GLOBAL DATA
	x[0] {
	    Y[] y; -----+----> [ Y.init ]
	}               |
	x[1] {          |
	    Y[] y; -----'
	}


>   x.writeln;       // [[10], [10]]
> 
>   x[0].y[0] = 0;   // [[0], [0]]

This line in essence says, "assign 0 to the first element of the array in the slice y, in the first element of array x".  Since both x[0].y and x[1].y point to the same underlying array, modifying it via x[0].y will also cause x[1].y to see the change.

IOW, the revised ASCII art diagram now looks like this:

	STACK                  GLOBAL DATA
	x[0] {
	    Y[] y; -----+----> [ 0 ]
	}               |
	x[1] {          |
	    Y[] y; -----'
	}

That is why x[0].y[0] == 0 and also x[1].y[0] == 0. This is because x[0].y.ptr == x[1].y.ptr.


>   x.writeln;
> 
>   x[0].y.length++;

This line tries to increase the length of the array pointed to by x[0].y. Since it was declared as a 1-element literal, which is allocated in program global data area, there isn't room to expand it.  So at this point, in order to honor the request to lengthen the array, druntime will allocate a new array on the heap and copy the contents of the old array over, then expand its length to 2.  The situation now looks like this:

	STACK                  GLOBAL DATA           HEAP
	x[0] {
	    Y[] y; --------------------------------> [ 0, 0 ]
	}
	x[1] {
	    Y[] y; ----------> [ 0 ]
	}

Key point: x[0].y and x[1].y now point to two different arrays, in two different places. Changes in one will no longer reflect in the other.

The array [ 0 ] shown above is the same array that x[0].y *used* to point to, but no longer does because druntime has made a copy of it in the heap and updated x[0].y to point to the copy instead of the original.  x[1].y, however, continues to point to the original array.


>   x[0].y[1] = 1;

This modifies the second element of x[0].y to 1. Situation now looks like this:

	STACK                  GLOBAL DATA           HEAP
	x[0] {
	    Y[] y; --------------------------------> [ 0, 1 ]
	}
	x[1] {
	    Y[] y; ----------> [ 0 ]
	}


>   x[1].y[0] = 2;

This modifies the first element of x[1].y to 2. Situation:

	STACK                  GLOBAL DATA           HEAP
	x[0] {
	    Y[] y; --------------------------------> [ 0, 1 ]
	}
	x[1] {
	    Y[] y; ----------> [ 2 ]
	}


>   x.writeln;       // [[0, 1], [2]]

Which reflects the situation shown above.


T

-- 
Why ask rhetorical questions? -- JC
October 25, 2022
On 10/25/22 17:16, Salih Dincer wrote:

> Excuse me, but they still write in purple prose about dynamic&static
> array literature here!

I've heard positive things about D's arrays from people who use D in production. Especially slices...

> I've known D for more than 10 years, but the topic we're talking about
> still seems strange to me.

Your example makes it more complicated and potentially exposes a bug.

> The explanations given are not enough for
> me, I'm sorry.

There may be a number of different concepts to list but I don't think there is anything inherently complicated with these topics (again, your example is more complicated).

> Can anyone tell me what happens when I change the location of the
> structure?

What you mean is, you see different behaviour depending on struct X is nested or not. The difference is, nested structs carry a context pointer. This may be related to a bug for the different outputs that we see.

> So the X structure must be in the stack when it is in
> main(), and the following must be in the heap, right?

To nit-pick: The struct is just a definition. Not the struct but its objects can be on the stack or on the heap.

But yes, all objects you have are on the stack.

> //void main() {

So when you uncomment that line and comment-out the following main() line in the program, you see a different output.

I tested: If you make X a 'static struct', then you see the same output.

I think the difference is due to a bug.

Ali

October 26, 2022
On Wednesday, 26 October 2022 at 00:44:45 UTC, H. S. Teoh wrote:
> 
> If you'll excuse some ASCII art, here's the situation you have:
>
> 	STACK                  GLOBAL DATA
> 	x[0] {
> 	    Y[] y; -----+----> [ Y.init ]
> 	}               |
> 	x[1] {          |
> 	    Y[] y; -----'
> 	}
>
>

Thanks for these detailed explanations, especially the ASCII art 😀

On Wednesday, 26 October 2022 at 00:58:33 UTC, Ali Çehreli wrote:
> On 10/25/22 17:16, Salih Dincer wrote:
> I tested: If you make X a 'static struct', then you see the same output.

It occurred to me too, to use a static struct.  I also tried the following example because it can work with static in main():

import std;

void main() {
  //static
  struct X
  {
    static struct Y {
   //...
  }}

  static
  struct Bar {
    string s;

    string toString() {
      return s;
    }
  }

  auto list = "sixtwoone".chunks(3);
       list.map!(c => c.to!string)
           .map!Bar.array.writeln; // [six, two, one]
   //...
}

Thank you...

@SDB79
October 25, 2022
On 10/25/22 19:25, Salih Dincer wrote:

> with static in main():

If 'static' makes a difference on your side as well, it is your turn to create a bug report. :) (Last time you discovered a bug, I was too quick to report it. :/)

Ali

October 26, 2022
On Wednesday, 26 October 2022 at 02:34:24 UTC, Ali Çehreli wrote:
> On 10/25/22 19:25, Salih Dincer wrote:
>
> > with static in main():
>
> If 'static' makes a difference on your side as well, it is your turn to create a bug report. :) (Last time you discovered a bug, I was too quick to report it. :/)

😀

On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote:
> Does the second piece of code shows a bug or my expectation is not correct (and why if so)?

As a result, if this is a bug, Andrey has the right to report it. Unlike what Andrey did, I haven't tried it with a nested struct.

There is also be in the heap or be in the stack issue...

SDB@79
October 26, 2022
On Wednesday, 26 October 2022 at 04:40:17 UTC, Salih Dincer wrote:
> On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote:
>> Does the second piece of code shows a bug or my expectation is not correct (and why if so)?
>
> As a result, if this is a bug, Andrey has the right to report it.

Bug tracking system doesn't work with gmail emails so I'm not able to report.


October 26, 2022
Looks like explicitly initialized variable in this case allocates array literal. Uninitialized variable is initialized with init pattern. This may be correct as uninitialized variable isn't guaranteed to hold a value most useful for you, it's only guaranteed to hold a defined value.
1 2 3
Next ›   Last »