Dlang associative array of an array of strings keyed by a string has unexpected behavior

38 Views Asked by At

The output from the following code does not behave how I think it should. It appears that making changes to the string array returned by testAA.require() does not reflect in the associative array. I thought dynamic arrays were pass by reference, but this seems to be behaving contrary to this notion. Re-assigning the array to testAA does appear to work, but the reason why this does not work is bothering me. Any tips or ideas would be appreciated!

module main;

import std.stdio;

void main()
{
  string[][string] testAA;
  string[] vals = ["InitVal1", "InitVal2"];

  auto list = testAA.require("Key", vals);

  writeln("list: ", list);

  list ~= "Val1";
  list ~= "Val2";

  writeln("list: ", list);
  writeln("testAA", testAA);
}

This results in the output:

list: ["InitVal1", "InitVal2"]
list: ["InitVal1", "InitVal2", "Val1", "Val2"]
testAA["Key":["InitVal1", "InitVal2"]]
3

There are 3 best solutions below

1
Steven Schveighoffer On BEST ANSWER

The call to update isn't necessary after the require, just do testAA["Key"] ~= "Val1". Note that this does not update list, which is again, still a copy of the array slice at the time it was added.

If you want to do it by reference, you need to use a pointer:

auto list = &testAA.require("Key", vals); // keep a pointer to the array
*list ~= "Val1";
*list ~= "Val2";

If I'm unsure if an AA has a key, I usually will do aa.require(key) ~= val;. This is a bit less nice than just indexing, but basically equivalent to indexing, putting in a value.init when it doesn't yet exist.

0
Element Green On

Someone pointed me to using the associative array update() method and also explained that a slice (dynamic array) consists of a pointer and a length, which are copied, even though the content itself is referenced. So the slice in the associative array does not update when the slice is updated external to it.

So I used this to create a new list with a single item or append a value to the list if it already exists:

testAA.update("Key", () => ["Val1"], (ref string[] x) {x ~= "Val1";});
3
Doigt On

Where in the docs does it say that associate arrays are passed by ref? If you want a ref, your function should have the ref keyword in the function signature. It's the same thing with normal arrays.