With the latest DMD versions it's easy to create an immutable array of structs,
with a pure function (I think this gives advantages similar to the Transients
of the Clojure language):
struct Foo {
int x;
bool b;
}
immutable(Foo[]) generate() pure nothrow {
auto arr = new Foo[10];
foreach (i; 0 .. 10)
arr[i].x = i;
arr[3] = Foo(3, true);
return arr;
}
void main() {
auto a = generate();
}
But _very_ often I have to create arrays of structs that have mixed
mutable/immutable fields. To create them I have to use append, that is OK for
little arrays, but it's not nice when the arrays get large and when the rules
to fill them become complex:
struct Foo {
int x;
immutable bool b;
}
Foo[] generate() pure nothrow {
Foo[] arr;
foreach (i; 0 .. 10) {
if (i == 3) // the rule to fill arr
arr ~= Foo(i, true);
else
arr ~= Foo(i, false);
}
return arr;
}
void main() {
auto a = generate();
}
As alternative, to avoid the append, I am able to use read-only properties for
the immutable fields, but this requires a more complex struct (and I think
currently DMD can't perform const-related optimizations on fileds like this
b__, because in D inside a module the "private" attribute is ignored. I think
this makes certain compiler optimizations harder, that are instead easy in C#
because "private" works at module level too, so a read-only property is similar
to a const value. I'd like to know more about this optimization-related
situation):
struct Foo {
int x;
private bool b__ = false;
// readonly
@property bool b() const pure nothrow { return b__; }
this(in int x_, in bool b_=false) pure nothrow {
this.x = x_;
this.b__ = b_;
}
}
Foo[] generate() pure nothrow {
auto arr = new Foo[10];
foreach (i; 0 .. 10)
arr[i].x = i;
arr[3] = Foo(3, true);
return arr;
}
void main() {
auto a = generate();
}
This kind of code (currently the type system raises an error here) is not safe
even using just "const" for the field "b", so it's not a solution, because
inside generate the const is broken/ignored, so the compiler must perform zero
const-related optimizations regarding the field b (and its sub-tree data):
struct Foo {
int x;
const bool b;
}
Foo[] generate() pure nothrow {
auto arr = new Foo[10];
foreach (i; 0 .. 10)
arr[i].x = i;
arr[3] = Foo(3, true);
return arr;
}
void main() {
auto a = generate();
}
I think code like this is a mess (this is kind of the opposite of the "nested
constantness" I discussed some time ago):
struct Foo {
int x;
const bool b;
}
Foo[] generate() pure nothrow {
with (mutable Foo.b) {
auto arr = new Foo[10];
foreach (i; 0 .. 10)
arr[i].x = i;
arr[3] = Foo(3, true);
}
return arr;
}
void main() {
auto a = generate();
}
At the moment I don't have further ideas on this.
Bye,
bearophile