www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Odd Associative Array Reference Behavior

reply Matt Elkins <notreal fake.com> writes:
Consider the following definition of Foo and an accompanying 
unittest:

[code]
struct Foo
{
      property int[int] aa() {return m_aa;}
      property ref int[int] aaRef() {return m_aa;}
     int[int] m_aa;
}

unittest
{
     Foo foo;
     assert(5 !in foo.m_aa); // Sanity-check to start off
     foo.aa[5] = 1;          // Add an element with key 5
     assert(5 !in foo.m_aa); // ...huh. 5 didn't make it in?
     foo.aaRef[5] = 1;       // Try again, using the ref variant
     assert(5 in foo.m_aa);  // Works!
}
[/code]

I was under the impression that associative arrays are reference 
types; if I pass a non-ref "copy" of one, shouldn't insertions 
still be reflected in the original? Am I dealing with a bug or a 
misunderstanding on my part?
Feb 10 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/10/16 10:10 PM, Matt Elkins wrote:
 Consider the following definition of Foo and an accompanying unittest:

 [code]
 struct Foo
 {
       property int[int] aa() {return m_aa;}
       property ref int[int] aaRef() {return m_aa;}
      int[int] m_aa;
 }

 unittest
 {
      Foo foo;
      assert(5 !in foo.m_aa); // Sanity-check to start off
      foo.aa[5] = 1;          // Add an element with key 5
      assert(5 !in foo.m_aa); // ...huh. 5 didn't make it in?
      foo.aaRef[5] = 1;       // Try again, using the ref variant
      assert(5 in foo.m_aa);  // Works!
 }
 [/code]

 I was under the impression that associative arrays are reference types;
 if I pass a non-ref "copy" of one, shouldn't insertions still be
 reflected in the original? Am I dealing with a bug or a misunderstanding
 on my part?
Misunderstanding. An AA under the hood is simply a pointer. Initialized to null. When you pass it around, you are passing a pointer. AA assign checks for null and allocates a new AA impl to hold the data. But this doesn't affect other copies (that were null). So what is happening is aa() returns a null AA. You assign to it, which allocates a new AA impl, and sets the rvalue to point at it. The rvalue promptly disappears. The original m_aa is still set to point at null. If you add more elements, you will see you can do so using the non-ref version. It's only on the first assignment that the reference changes. After that, it's the same reference forever (unless reassigned of course). -Steve
Feb 10 2016
parent Matt Elkins <notreal fake.com> writes:
On Thursday, 11 February 2016 at 03:47:09 UTC, Steven 
Schveighoffer wrote:
 Misunderstanding.

 An AA under the hood is simply a pointer. Initialized to null.

 When you pass it around, you are passing a pointer. AA assign 
 checks for null and allocates a new AA impl to hold the data. 
 But this doesn't affect other copies (that were null).

 So what is happening is aa() returns a null AA. You assign to 
 it, which allocates a new AA impl, and sets the rvalue to point 
 at it. The rvalue promptly disappears. The original m_aa is 
 still set to point at null.
Makes sense (though it defies my intuition; I would have expected an NPE or crash at time of assignment). Thanks!
Feb 10 2016