Re: Is "auto t=T();" not the same as "T t;"?

2022-10-26 Thread Kagamin via Digitalmars-d-learn
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.


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-26 Thread Andrey Zherikov via Digitalmars-d-learn

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.





Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Salih Dincer via Digitalmars-d-learn

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


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

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



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Salih Dincer via Digitalmars-d-learn

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


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

On 10/25/22 17:16, Salih Dincer wrote:

> Excuse me, but they still write in purple prose about dynamic
> 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



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread H. S. Teoh via Digitalmars-d-learn
On Wed, Oct 26, 2022 at 12:16:55AM +, 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


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Salih Dincer via Digitalmars-d-learn

On Tuesday, 25 October 2022 at 20:36:28 UTC, matheus wrote:

On


int[] a = [1];
int[] b = a.dup;

assert([0] !is [0]); // different memory
```


This is interesting, I understand the point of "reference vs 
copy", and I'm OK with this design choice of, but I wonder in 
the case of newcomers if this is a case the generate more 
problem understanding this rules, like we are having here.


Matheus.


Huh, I do NOT understand and I DO agree with you!

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


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];

string toString() {
  return y.format!"%s";
}
  }

void main() {

  X[2] x;
  x.writeln;   // [[10], [10]]

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

  x[0].y.length++;
  x[0].y[1] = 1;

  x[1].y[0] = 2;
  x.writeln;   // [[0, 1], [2]]

} /* Output of other state:
[[10], [10]]
[[0], [0]]
[[2, 1], [2]]
*/
```
SDB@79


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

On 10/25/22 13:36, matheus wrote:
> On Tuesday, 25 October 2022 at 20:12:25 UTC, Paul Backus wrote:

>> Static arrays are value types.

What that means is, when we say float[3], there are just 3 floats 
without any overhead.


>> Dynamic arrays are reference types.

That phrase can be confusing because we often use the terms "dynamic 
array" and "slice" interchangably.


I find the following simpler to understand (can still be confusing):

- Dynamic arrays are expandable arrays that are owned by the D runtime 
(the GC). Dynamic arrays don't have names.


- The names (symbols) that we see in source code are slices that are 
references to elements of arrays. Such arrays can be static or dynamic.


When we add an element to a slice that has no room (.capacity <= 
.length) then a fresh dynamic array is created from the copies of the 
elements of the slice.


> if this is a case the generate more problem understanding this rules,

I think all these array complexities are inherent. Other examples from 
two other languages that I know:


- In C, arrays that are members of user-defined types are value types. 
Arrays that are parameters are reference types (pointer to the first 
element). Confusing.


- In C++, std::vector is a value type, which would be copied if passed 
by value. So there came std::array, std::string_view, std::span, etc. to 
address value versus reference complexities.


I don't think D could do anything better with arrays. They seem to work 
pretty well and are among the favorite features of many D programmers.


Ali



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Paul Backus via Digitalmars-d-learn

On Tuesday, 25 October 2022 at 20:27:18 UTC, Ali Çehreli wrote:

On 10/25/22 13:12, Paul Backus wrote:

> In order to create a copy of a static array

Although .dup works for static arrays as well, you meant 
"dynamic array" and everyones knows it. :)


Yes; thank you for the correction. :)


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread matheus via Digitalmars-d-learn

On Tuesday, 25 October 2022 at 20:12:25 UTC, Paul Backus wrote:

On Tuesday, 25 October 2022 at 17:54:16 UTC, Salih Dincer wrote:

On Tuesday, 25 October 2022 at 17:18:35 UTC, Paul Backus wrote:
It's not a bug. They're pointing to the exact same instance 
of `A` in memory:


I don't understand?  So I don't understand why it causes 
problems with dynamic arrays!  So why is there nothing wrong 
with the static array in the example below?


Static arrays are value types. When you copy a static array, 
the copy's data is stored in a separate block of memory from 
the original:


```d
int[1] a = [1];
int[1] b = a;

assert([0] !is [0]); // different memory
```

Dynamic arrays are reference types. When you copy a dynamic 
array, both copies point to the same block of memory:


```d
int[] a = [1];
int[] b = a;

assert([0] is [0]); // same memory
```

In order to create a copy of a static array with its own block 
of memory, separate from the original, you have to use the 
built-in `.dup` method:


```d
int[] a = [1];
int[] b = a.dup;

assert([0] !is [0]); // different memory
```


This is interesting, I understand the point of "reference vs 
copy", and I'm OK with this design choice of, but I wonder in the 
case of newcomers if this is a case the generate more problem 
understanding this rules, like we are having here.


Matheus.


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

On 10/25/22 13:12, Paul Backus wrote:

> In order to create a copy of a static array

Although .dup works for static arrays as well, you meant "dynamic array" 
and everyones knows it. :)


> with its own block of
> memory, separate from the original, you have to use the built-in `.dup`
> method:
>
> ```d
> int[] a = [1];
> int[] b = a.dup;

Ali



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Paul Backus via Digitalmars-d-learn

On Tuesday, 25 October 2022 at 17:54:16 UTC, Salih Dincer wrote:

On Tuesday, 25 October 2022 at 17:18:35 UTC, Paul Backus wrote:
It's not a bug. They're pointing to the exact same instance of 
`A` in memory:


I don't understand?  So I don't understand why it causes 
problems with dynamic arrays!  So why is there nothing wrong 
with the static array in the example below?


Static arrays are value types. When you copy a static array, the 
copy's data is stored in a separate block of memory from the 
original:


```d
int[1] a = [1];
int[1] b = a;

assert([0] !is [0]); // different memory
```

Dynamic arrays are reference types. When you copy a dynamic 
array, both copies point to the same block of memory:


```d
int[] a = [1];
int[] b = a;

assert([0] is [0]); // same memory
```

In order to create a copy of a static array with its own block of 
memory, separate from the original, you have to use the built-in 
`.dup` method:


```d
int[] a = [1];
int[] b = a.dup;

assert([0] !is [0]); // different memory
```


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

On 10/25/22 11:23, Steven Schveighoffer wrote:

>> Why do I say incorrect things like that? :)

> You were right actually.

As always! Now I'm confused. :o)

Ali



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Steven Schveighoffer via Digitalmars-d-learn

On 10/25/22 2:03 PM, Ali Çehreli wrote:

On 10/25/22 11:01, Ali Çehreli wrote:

 > static arrays don't have .ptr to point
 > to any member.

Why do I say incorrect things like that? :) Of course static arrays have 
.ptr as well but that always point to their own body of N elements. They 
own their elements... I move away from the keyboard now... :)


You were right actually. The `arr.ptr` is a shortcut for the address of 
the first element.


A dynamic array is equivalent to a struct with a `ptr` and `length` 
fields. A static array does not have fields, it's just the data itself. 
The properties are not real fields, but lowerings so they can be used 
like dynamic arrays.


-Steve


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

On 10/25/22 11:01, Ali Çehreli wrote:

> static arrays don't have .ptr to point
> to any member.

Why do I say incorrect things like that? :) Of course static arrays have 
.ptr as well but that always point to their own body of N elements. They 
own their elements... I move away from the keyboard now... :)


> Static arrays are "just elements side by side."

I still like that description though.

Ali



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

On 10/25/22 10:54, Salih Dincer wrote:

> So I don't understand why it causes problems with
> dynamic arrays!  So why is there nothing wrong with the static array in
> the example below?

The same rules as other uses of dynamic arrays...

>  //A[] a = [A.init];/*

In that case, there is a single instance of [A.init]. All A.a members 
point to it through their .ptr .length members.


>  A[1] a = [A.init];//*/

In that case, there is still a single instance of [A.init], which gets 
copied to each A.a member because static arrays don't have .ptr to point 
to any member. Static arrays are "just elements side by side."


In other words, there is no option but to copy to static arrays; they 
can't point to elements and they don't have any problem in this case.


Ali



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Salih Dincer via Digitalmars-d-learn

On Tuesday, 25 October 2022 at 17:18:35 UTC, Paul Backus wrote:
It's not a bug. They're pointing to the exact same instance of 
`A` in memory:


I don't understand?  So I don't understand why it causes problems 
with dynamic arrays!  So why is there nothing wrong with the 
static array in the example below?



```d
import std.stdio, std.format;
void main()
{
  struct B
  {
struct A {
  int i = 10;
}
//A[] a = [A.init];/*
A[1] a = [A.init];//*/

string toString() {
  return a[0].i.format!"%s";
}
  }

  B[2] b;
  b.writeln;  // [10, 10]

  b[0].a[0].i = 0;
  b.writeln;  // [0, 10]

  b[1].a[0].i = 1;
  b.writeln;  // [0, 1]
}
```


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

On 10/25/22 08:50, Andrey Zherikov wrote:

> I'd like to tune default ctor but structs can't have custom one.
> Adding a ctor with parameter seems a hack to me

There is static opCall, which may be seen as a hack as well. :) The 
following all print the same thing now:


import std.stdio;

struct A
{
int[] i;
}
struct B
{
A[] a = [A.init];

static B opCall()
{
return B.init;
}
}
void main()
{
auto b1 = B.init;
b1.writeln;

B b2 = B();
b2.writeln;

B b3;
b3.writeln;
}

> This fails in run time

Not anymore with the above code.

Ali



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Paul Backus via Digitalmars-d-learn

On Tuesday, 25 October 2022 at 16:52:48 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)?


This is a bug:

```d
void main()
{
  struct B
  {
struct A
{
  int i = 10;
}
A[] a = [A.init];
  }

  B[2] b;
  assert(b[0].a[0].i == 10);
  assert(b[1].a[0].i == 10);

  b[0].a[0].i = 1;
  assert(b[0].a[0].i == 1); // ok...
  assert(b[1].a[0].i == 1); // must be 10 !!!
}
```


It's not a bug. They're pointing to the exact same instance of 
`A` in memory:


```d
void main()
{
  struct B
  {
struct A
{
  int i = 10;
}
A[] a = [A.init];
  }

  B[2] b;
  assert(b[0].a.ptr is b[1].a.ptr);
}
```

As explained in [Adam's reply][1], what happens here is that 
there is a single, global `A[]` allocated at compile time, which 
is shared between all instances of `B.init`. It's the same as if 
you'd written


```d
struct B
{
struct A
{
int i = 10;
}
static A[] globalArray = [A.init];
A[] a = globalArray;
}
```

[1]: 
https://forum.dlang.org/post/yznhocajstphrozpn...@forum.dlang.org


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Salih Dincer via Digitalmars-d-learn
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)?


This is a bug:

```d
void main()
{
  struct B
  {
struct A
{
  int i = 10;
}
A[] a = [A.init];
  }

  B[2] b;
  assert(b[0].a[0].i == 10);
  assert(b[1].a[0].i == 10);

  b[0].a[0].i = 1;
  assert(b[0].a[0].i == 1); // ok...
  assert(b[1].a[0].i == 1); // must be 10 !!!
}
```

SDB@79


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Andrey Zherikov via Digitalmars-d-learn

On Tuesday, 25 October 2022 at 14:53:50 UTC, Adam D Ruppe wrote:
On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov 
wrote:

A[] a = [A.init];


This is a problem - this is referring to a static array 
instance, shared across all copies of B. You almost certainly 
don't want this.


That B.a[0] is the *same object* across different 
default-constructed Bs... unless the optimizer hits it or 
something.


Is it a bad idea to trigger copy on write before modification of 
B.a so it behaves as below?

```d
B b1;
b1.a = b1.a.dup;   // copy on write
b1.a[0].i ~= '1';
b1.a ~= A.init;
b1.a[0].i ~= '2';
b1.a[1].i ~= '3';
b1.writeln;// B([A("12"), A("3")])

B b2;
b2.writeln;// B([A("")])
```


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Andrey Zherikov via Digitalmars-d-learn

On Tuesday, 25 October 2022 at 14:53:50 UTC, Adam D Ruppe wrote:
But just don't do this. Only basic values and immutable strings 
are good to initialize this way. With the array or class 
objects, you're liable to get some shared thing.


If you change this to be initialized in a constructor (which 
will require an argument in D) or factory function, you'll get 
far more consistent behavior as each instance will have its own 
array.


I'd like to tune default ctor but structs can't have custom one.
Adding a ctor with parameter seems a hack to me - and compiler 
still allows default construction even when default ctor is 
disabled:


```d
struct A
{
int[] i;
}
struct B
{
A[] a;
@disable this();
this(bool)
{
A[] a = [A.init];
}
}
void main()
{
auto b1 = B.init;
b1.a[0].i ~= 1;
b1.a ~= A.init;
b1.a[0].i ~= 11;
b1.a[1].i ~= 12;
b1.writeln;

auto b2 = B.init;
b2.writeln;
}
```
This fails in run time, not compile time:
```
core.exception.ArrayIndexError@onlineapp.d(19): index [0] is out 
of bounds for array of length 0


??:? _d_arraybounds_indexp [0x55b558b8ec55]
./onlineapp.d:19 _Dmain [0x55b558b6bd5f]
```

As for why B() and B.init are different here... i don't know, 
probably some subtly of the compiler's implementation.


Actually `auto b = B.init;` behaves the same way as `auto b = 
B();` but they don't do the same as `B b;`


Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Ali Çehreli via Digitalmars-d-learn

On 10/25/22 07:53, Adam D Ruppe wrote:
> On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov wrote:
>> A[] a = [A.init];
>
> This is a problem - this is referring to a static array instance, shared
> across all copies of B. You almost certainly don't want this.

Agreed. It should be fine when the elements are immutable as I've 
experimented with recently[1]:


struct Xml {
string beg;
string end = "/>";  // Shared by all instances;
// no allocation for some objects.
// ...
}

> As for why B() and B.init are different here... i don't know, probably
> some subtly of the compiler's implementation.

I think it's a bug.

Ali

[1] https://youtu.be/0JL9uT_XGZE?t=4260s



Re: Is "auto t=T();" not the same as "T t;"?

2022-10-25 Thread Adam D Ruppe via Digitalmars-d-learn
On Tuesday, 25 October 2022 at 13:51:30 UTC, Andrey Zherikov 
wrote:

A[] a = [A.init];


This is a problem - this is referring to a static array instance, 
shared across all copies of B. You almost certainly don't want 
this.


That B.a[0] is the *same object* across different 
default-constructed Bs... unless the optimizer hits it or 
something.


But just don't do this. Only basic values and immutable strings 
are good to initialize this way. With the array or class objects, 
you're liable to get some shared thing.


If you change this to be initialized in a constructor (which will 
require an argument in D) or factory function, you'll get far 
more consistent behavior as each instance will have its own array.


As for why B() and B.init are different here... i don't know, 
probably some subtly of the compiler's implementation.