Bug 6633 – Mixed mutable/immutable struct array creation

Status
RESOLVED
Resolution
INVALID
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2011-09-09T02:20:48Z
Last change time
2021-01-24T05:48:00Z
Assigned to
No Owner
Creator
bearophile_hugs

Comments

Comment #0 by bearophile_hugs — 2011-09-09T02:20:48Z
This enhancement request doesn't show a desired solution, it just shows a problem and some ideas to solve it. Suggestions are welcome. In my opinion the creation of arrays of structs that contain a mix of mutable/immutable fields is a common need. The immutable fields are useful to represent data that's invariant in the data structure (like the topology of a grid, etc), while the mutable fields represent the parts that change (like the values associated to that grid nodes). There are some different ways to create such arrays, but no one is really good. This way doesn't work for fixed-sized arrays and is not good for larger arrays: 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(); } I have discussed some alternative solutions here: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=144146 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=144149 From the answers received in that thread, and further polishing, I see three alternative good enough solutions, shown below. --------------------- Here Timon Gehr: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=144177 Suggests no changes no changes in D: > I think the compiler just needs to be smart enough to understand that > after the execution of code of the form: > > T[] a; > foreach(i;x..y) { > if(c1) a~=v1; > else if(c2) a~=v2; > ... // exactly one one-element-append per branch > } > > the array a will have length y-x. Maybe value range propagation can help. Downsides: - The compiler has to enforce all code paths add exactly one item to the array, like in the "missing return statement" error. - It sounds a bit weird to contraint the code like that. Upsides: - No visible changes neeed to D. --------------------- In the same post Timon Gehr has suggested a better idea, with a raw still syntax: > What about > > void generate(this Foo[] self, int len) this{ // maybe there is a better syntax > // init self > } > > ? > > This would just have the same restrictions as a class/struct constructor > that initializes immutable fields. Alternative: > void generate(this out Foo[] self, int len) this { This is a nice idea. Currently you are not allowed to initialize const arrays in a struct ctor, but I think this will be fixed and allowed: struct Foo { const int i; const int[3] array; this(int x) { this.i = x; this.array[0] = x; // Error: this.array[0] isn't mutable } } Downsides: - Requires some change to D, and some specific syntax. - Inside the "constructor" the const is not enforced, so you need to be careful. But this is not different from using 'x' field in that struct ctor. - I have not seen a good enough syntax yet for this idea. Upsides: - The semantics of this idea seems clean enough, and it follows something that is (will be) already present in D. - You are free to build your array as you want, no constraints on the code you use, and it's efficient. Another bad syntax idea: struct Foo { int x; const bool b; } void generate(this Foo[], int len) pure nothrow { this = new Foo[len]; foreach (i; 0 .. len) this[i].x = i; this[3] = Foo(3, true); } void main() { auto a = generate(10); } --------------------- Andrei Alexandrescu has written a comment about use of arrays of structs that use @disable: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=143439 > The introduction of @disable requires the > introduction of array-with-constructor syntax that has long been > proposed to both C++ and D: > > new Type[n](argument1, argument2, ..., argumentn) > > creates an array of Type of size n. Each element is constructed with > argument list (argument1, argument2, ..., argumentn). With some changes this idea becomes useful for my problem too. The idea is to support some syntax to initialize array items. The two useful situations are: - Give a constant value. This special case is supported because it's very common and it needs to be fast. - Give a delegate that builds the array item, this delegate takes the index or indexes of the array item as argument. A 1D array requires a delegate with 1 index argument, a 2D array requires a delegate with 2 index arguments, etc. An idea: struct Foo { int x; immutable bool b; } Foo generate(size_t i) pure nothrow { if (i == 3) return Foo(i, true); else return Foo(i, false); } void main() { auto a = new Foo[](10)(&generate); //auto a = new Foo[10](&generate); // ? //auto a = new Foo[10]{ &generate }; // ? } Downsides: - calling a function/delegate for each array item is not so efficient, unless the compiler performs inlining. - The syntax to pass the argument to the new array is a bit critical. So I have given the generator in a second set of parentheses. - It's not so flexible, you can't update items more than once, you can't read other array items, etc. Upsides: - It doesn't require a lot of changes in DMD - I think it's usable for arrays with @disable too - The syntax is easy enough to use, but it clashes a little with the normal array syntax.
Comment #1 by maxhaton — 2021-01-24T05:48:00Z
Not a bug with dmd; discussion is superseded