www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Changing behavior of associative array

reply Kevin Bailey <keraba yahoo.com> writes:
Perhaps someone can help solve this mystery. I have a sample 
program that adds structs to an associative array (as keys) in a 
subroutine. The program passes the array by reference as expected 
(below).

My real program does the same thing and yet the associative array 
is passed by value. Thus anything added in the subroutine is lost.

I've added a TON of writeln's to confirm this. The *instant* I 
made the parameter "ref", the real program starts acting as 
expected, and the print-outs show the correct results.

Does anyone know why the behavior would change? Has anyone seen 
anything like this?

I even tried importing the same libraries into both programs. The 
only remaining difference is that the real function has several 
more arguments (7), but I would be shocked if D changed its 
behavior because of that.

thx
```
import std.stdio;

struct Foo
{
     uint a, b, c;
}

void add_one_and_recurse(
     uint[Foo] m,
     int x)
{
     if (!x)
         return;
     foreach (k,v; m)         // Prints the 1 from main()
         writeln(k, ", ", v); // as expected.
     add_one_and_recurse(m, x-1);
     Foo f = {4, 5, x};
     m[f] = 7;
}

int main()
{
     uint[Foo] m;
     Foo f = {1, 2, 3};
     m[f] = 1;
     add_one_and_recurse(m, 3);
     foreach (k,v; m)         // Prints 4 items
         writeln(k, ", ", v); // as expected.
     return 0;
}
```
Dec 16 2023
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 16 December 2023 at 20:04:54 UTC, Kevin Bailey wrote:
 I've added a TON of writeln's to confirm this. The *instant* I 
 made the parameter "ref", the real program starts acting as 
 expected, and the print-outs show the correct results.
If you comment out this line ``` // m[f] = 1; ``` in your main function of your posted code you can catch up with your real programm insofar as you now need a ref parameter here, too.
 Does anyone know why the behavior would change? Has anyone seen 
 anything like this?
Sure.
Dec 16 2023
parent reply Dennis <dkorpel gmail.com> writes:
On Saturday, 16 December 2023 at 21:30:55 UTC, kdevel wrote:
 If you comment out this line

 ```
 //    m[f] = 1;
 ```

 in your main function of your posted code you can catch up with 
 your
 real programm insofar as you now need a ref parameter here, too.
That's because `m[f] = 1` initializes the associative array to something non-null. If you pass a `null` AA to a function which adds things, the caller will still have a null pointers. You can initialize a non-null empty AA like this: ```D uint[Foo] m = new uint[Foo]; ``` Then, `m` can be passed by value and you can make additions or removals which the caller sees, unless you assign a new AA in the function.
Dec 16 2023
next sibling parent Julian Fondren <julian.fondren gmail.com> writes:
On Saturday, 16 December 2023 at 22:44:16 UTC, Dennis wrote:
 That's because `m[f] = 1` initializes the associative array to 
 something non-null. If you pass a `null` AA to a function which 
 adds things, the caller will still have a null pointers.
I've gotten this error in deployed Perl. Whenever the ceremony of creating a data structure is reduced, people can lose sight of when that happens. Here's a less common gotcha: ```d void main() { import std.stdio : writeln; int force_realloc(ref int[] seq) { foreach (int i; 1 .. 1_000_000) { seq ~= i; } return 1234; } int[] a = [4321]; writeln(a[0]); // 4321, of course a[0] = force_realloc(a); writeln(a[0]); // still 4321 } ``` The `a[0]` on the left of the assignment is decided early, and then force_realloc() changes what the location should be.
Dec 16 2023
prev sibling parent reply Kevin Bailey <keraba yahoo.com> writes:
On Saturday, 16 December 2023 at 22:44:16 UTC, Dennis wrote:
 That's because `m[f] = 1` initializes the associative array to 
 something non-null. If you pass a `null` AA to a function which 
 adds things, the caller will still have a null pointers. You 
 can initialize a non-null empty AA like this:

 ```D
 uint[Foo] m = new uint[Foo];
 ```

 Then, `m` can be passed by value and you can make additions or 
 removals which the caller sees, unless you assign a new AA in 
 the function.
Thanks Dennis. I'll have to tweak my mental model of what's happening. I was picturing a std::map-like thing that was passed by reference, but instead it seems like 'm' is a pointer to a std::map, that is initialized on use if null. (I think it's that latter part that gives the illusion of being already initialized.)
Dec 16 2023
parent Julian Fondren <julian.fondren gmail.com> writes:
On Sunday, 17 December 2023 at 00:10:56 UTC, Kevin Bailey wrote:
 instead it seems like 'm' is a pointer to a std::map, that is 
 initialized on use if null. (I think it's that latter part that 
 gives the illusion of being already initialized.)
Yes: https://dlang.org/spec/hash-map.html#construction_and_ref_semantic
Dec 16 2023