Comment #0 by qs.il.paperinik — 2022-09-26T09:42:03Z
An enum of slice of class can be mutated and it can be mutated even in a `pure` function.
Example:
class C
{
string s;
this(string a) pure @safe nothrow { s = a; }
}
enum C[] cs = [ new C("a"), new C("b") ];
void f() pure @safe @nogc nothrow
{
cs[0].s = "c";
}
Comment #1 by dkorpel — 2022-09-26T09:59:57Z
I thought it would allocate a new array literal in f(), but it really modifies global state:
```
class C
{
string s;
this(string a) pure @safe nothrow { s = a; }
override string toString() {return s;}
}
enum C[] cs = [ new C("a"), new C("b") ];
void f() pure @safe @nogc nothrow
{
cs[0].s = "c";
}
import std.writeln;
void main()
{
writeln(cs); // [a, b]
f();
writeln(cs); // [c, b]
}
```
I think the could should be accepted without @nogc, and it would modify a copy of the array.
Comment #2 by qs.il.paperinik — 2022-09-26T12:28:42Z
As enum was intended to be a replacement of C’s `#define` constants, the compiler should thoroughly treat `enum` as a named literal; it can support “mutable” indirections, but a lot more has to be taken. By “mutable” I mean typed non-const, not that actual mutation is valid.
An easy way would be to specify that enums are deep-copied at usage site. This might be possible because circular definitions are rejected:
class C
{
C bestie;
this() { }
this(C friend) { bestie = friend; }
}
enum C me = new C(you);
enum C you = new C(me); // error
enum C[] us = [ me, you ];
But you can cheese it:
enum C[] us = [ new C, new C ];
shared static this()
{
us[0].bestie = us[1];
us[1].bestie = us[0];
}
void main()
{
assert(us[0].bestie is us[1]);
assert(us[1].bestie is us[0]);
assert(us[0].bestie.bestie is us[0]);
assert(us[1].bestie.bestie is us[1]);
}
Comment #3 by robert.schadek — 2024-12-13T19:24:48Z