std.container.Array has reference semantics. Array.init basically is a null reference, but it's not an error to use it since it will initialize itself behind your back. However if you assign this reference somewhere it still behaves like null in the sense that you get two references to two conceptually distinct Arrays:
---------
void main()
{
import std.container, std.stdio;
{
/// Example 1
writeln("Example 1");
// to create an empty array, you can use Array.init
Array!int array1;
array1.insertBack(0);
// since Array offers reference semantics, you can make use of them
auto refTo1 = array1;
refTo1.insertBack(1);
// so this prints [0, 1], as expected
writeln(array1[]);
}
{
/// Example 2
writeln("\nExample 2");
// however that only works if you triggered array1 to be initialized,
// before assigning to refTo1
// create an empty array
Array!int array1;
// get a second reference to it, before doing anything meaningful
auto refTo1 = array1;
// and fill it now
array1.insertBack(0);
refTo1.insertBack(1);
// does not work as expected
// prints array1: [0]\nrefTo1: [1] instead of
// array1: [0, 1]\nrefTo1: [0, 1]
writefln("array1: %s", array1[]);
writefln("refTo1: %s", refTo1[]);
}
}
---
While I do think that using Array.init should assert, I do recognize that we probably cannot change this anymore. This should work though if I use make!Array to construct the arrays, but does not:
---
{
/// Example 1
writeln("Example 1");
// to create an empty array, you could use make as well
Array!int array1 = make!(Array!int);
array1.insertBack(0);
// since Array offers reference semantics, you can make use of them
auto refTo1 = array1;
refTo1.insertBack(1);
// so this prints [0, 1], as expected
writeln(array1[]);
}
{
/// Example 2
writeln("\nExample 2");
// however that only works if you triggered array1 to be initialized,
// before assigning to refTo1
// create an empty array
Array!int array1 = make!(Array!int);
// get a second reference to it, before doing anything meaningful
auto refTo1 = array1;
// and fill it now
array1.insertBack(0);
refTo1.insertBack(1);
// does not work as expected
// prints array1: [0]\nrefTo1: [1] instead of
// array1: [0, 1]\nrefTo1: [0, 1]
writefln("array1: %s", array1[]);
writefln("refTo1: %s", refTo1[]);
}
---
make(Container) should return an empty array, not the equivalent of a null reference. This is inconsistent to class based containers as well, where make returns a 'newed' container.
This would be an easy fix to make if we had a consistent way to force a struct based container to become initialized. Inserting and removing an element is ugly. In case of std.container.Array we could explicitly set length to 0, but thats undocumented behaviour.
Comment #1 by robert.schadek — 2024-12-01T16:23:23Z