Jump to page: 1 25  
Page
Thread overview
Array literals are weird.
May 01
Blatnik
May 01
evilrat
May 01
Blatnik
May 01
evilrat
May 01
russhy
May 02
evilrat
May 02
russhy
May 04
Blatnik
May 04
norm
May 04
norm
May 04
Blatnik
May 01
Blatnik
May 01
Blatnik
May 01
Blatnik
May 01
Blatnik
May 01
Dennis
May 05
user1234
May 05
user1234
May 05
user1234
May 05
user1234
May 01

I love how D redesigned the terrible C/C++ arrays. And it's awesome that array operations like element wise +, -, etc. are built into the language - it makes me want to use arrays and slices for everything. But there are still some rough edges in the design.

What do you expect to happen when you run this:

import std;
void main() {
  writeln(typeid([1, 2, 3]));
}

Maybe I'm crazy, but I would expect int[3] to be printed. But int[] gets printed instead.

Why? What's the reason for this design?

This automatic downconversion loses potentially useful type information by default. It also means lazy code like this:

auto array = [1, 2, 3]; // Type deduced as int[], not int[3]

performs an allocation, and thus it can't be used in @nogc or -betterC. (Not to mention that it's a completely unnecessary memory allocation).

It also means that functions that take a slice parameter:

void foo(int[] bar);

Can't be called naturally in @nogc or -betterC.

foo([1, 2, 3]) // Error: Array literal may cause GC allocation.

Instead you have to do this:

int[3] bar = [1, 2, 3];
foo(bar); // Now it works with @nogc and -betterC

This obviously isn't a huge deal but it introduces a bit of friction into the @nogc workflow. It also makes slower, allocating code easier to write by default in non @nogc code.

This decision to make array literals slice types by default seems like a bad idea in all respects. It should be changed, array literals (are) should be fixed size.

But I'm still new to D, so maybe there is something that I'm missing. Any thoughts?

May 01

On Saturday, 1 May 2021 at 11:32:47 UTC, Blatnik wrote:

>

It also means that functions that take a slice parameter:
Can't be called naturally in @nogc or -betterC.

There's several other options you can consider, including making foo take a static array, in which case this syntax just works, or an array variadic:

foo(int[] a...)

when you can call it like this:

foo(1, 2, 3)

or you can use the library .staticArray thing to expressly indicate your intention on the original foo

foo([1,2,3].staticArray);

>

This obviously isn't a huge deal but it introduces a bit of friction into the @nogc workflow. It also makes slower, allocating code easier to write by default in non @nogc code.

It also makes you less likely to accidentally write use-after-free code that refers to an array on the stack after the function returns...

There's some new features that are supposed to help catch this even with static arrays (the -dip1000 switch) but that's not terribly widespread.

>

But I'm still new to D, so maybe there is something that I'm missing. Any thoughts?

Well, a lot of this is just history: D didn't have static arrays like it does now for a while, but a good part of it too is to make the default thing be less likely to cause crash problems by accidentally escaping a reference to the stack.

May 01

On Saturday, 1 May 2021 at 11:32:47 UTC, Blatnik wrote:

>

I love how D redesigned the terrible C/C++ arrays. And it's awesome that array operations like element wise +, -, etc. are built into the language - it makes me want to use arrays and slices for everything. But there are still some rough edges in the design.

[...]

Yeah, imo this is a rather strange limitation of the language.

I would like to be able to do

int[] a = [1,2,3] * [4,5,6];

for example, but this yields "array operation without destination memory not allowed" :(

May 01

On Saturday, 1 May 2021 at 11:50:27 UTC, Adam D. Ruppe wrote:

>

or you can use the library .staticArray thing to expressly indicate your intention on the original foo

foo([1,2,3].staticArray);

Is there one in Phobos? Anyway this works, but maybe it can be made more type safe with constraints or just with improved symtax.

// compiles with -vgc and -betterC (weird, also without import stdc.core.stdio)
import std.range : ElementType;

template staticArray(alias T)
{
	enum ElementType!(typeof(T))[T.length] staticArray = T;
}

extern(C) void main()
{
	import core.stdc.stdio;
	auto arr = staticArray!([1,2,3]);
	pragma(msg, typeof(arr)); // int[3]
	foreach(i; arr)
		printf("%d\n", i);
}
May 01

On Saturday, 1 May 2021 at 12:22:50 UTC, evilrat wrote:

>

Is there one in Phobos?

http://dpldocs.info/staticArray

i haven't tested with -betterC restrictions but i don't see any reason why it wouldn't work there

May 01

On Saturday, 1 May 2021 at 11:50:27 UTC, Adam D. Ruppe wrote:

>

There's several other options you can consider, including making foo take a static array

Yea this does work, but some functions really do need to take many different array sizes. And in that case I guess you could make the array size a template parameter. But this would lead to template bloat for no reason in most cases.

>

or an array variadic:

foo(int[] a...)

This doesn't really work. It has the obvious problem of not being able to pass 2 different arrays like this.

>

or you can use the library .staticArray thing to expressly indicate your intention on the original foo

But this does work! I didn't know std.array had this. Thanks.

>

but a good part of it too is to make the default thing be less likely to cause crash problems by accidentally escaping a reference to the stack.

You mean stuff like:

int[] bug() {
  return [1, 2, 3];
}

int[] a = bug();
a[0] = 42; // Works fine now, but use-after-free-ish if array literals are static arrays.

Yea I can see how that would be a problem if array literals were static arrays by default. But even right now the compiler is able to catch this mistake pretty easily:

int[] bug() {
  int[3] a = [1, 2, 3];
  return a; // Error: Returning `a` escapes a reference to a local variable.
}

So the .staticArray thing is great but wouldn't it be even better if we didn't have to import a library and type that in.

I still see no reason [1, 2, 3] should ever not be considered a int[3], unless you explicitly ask for it:

auto x = [1, 2, 3]; // This really shouldn't allocate anything.
auto y = [1, 2, 3].dup; // I explicitly asked for it, please allocate!
May 01

On Saturday, 1 May 2021 at 12:27:07 UTC, Adam D. Ruppe wrote:

>

i haven't tested with -betterC restrictions but i don't see any reason why it wouldn't work there

Yep, [1,2,3].staticArray works just fine with -betterC.

May 01

On Saturday, 1 May 2021 at 12:27:07 UTC, Adam D. Ruppe wrote:

>

On Saturday, 1 May 2021 at 12:22:50 UTC, evilrat wrote:

>

Is there one in Phobos?

http://dpldocs.info/staticArray

i haven't tested with -betterC restrictions but i don't see any reason why it wouldn't work there

Wow, I see. It uses templates to deduce type and length as parameters.

May 01

On Saturday, 1 May 2021 at 12:27:30 UTC, Blatnik wrote:

>

Yea I can see how that would be a problem if array literals were static arrays by default. But even right now the compiler is able to catch this mistake pretty easily:

Yeah, it works now but that's again relatively new. It wasn't there when this stuff was designed.

It'd probably break a lot of code if this were to change now.

>

auto y = [1, 2, 3].dup; // I explicitly asked for it, please allocate!

yeah that'd probably be ok.

May 01

On Saturday, 1 May 2021 at 12:35:00 UTC, Adam D. Ruppe wrote:

>

It'd probably break a lot of code if this were to change now.

But the compiler could probably catch the mistake everywhere it happens with DIP 1000. And then it's just a matter of replacing [...] with [...].dup where you actually need it.

Although to be fair, even though the compiler (dmd) catches really simple cases, it doesn't catch more complex ones:

int[] bug1() {
  int[3] a = [1, 2, 3];
  return a; // Error: Returning `a` escapes reference to a local variable.
}

int[] bug2() {
  int[3] a = [1, 2, 3];
  int[] b = a;
  return b; // No error!
}

But with DIP 1000 this is caught as well:

int[] bug2() {
  int[3] a = [1, 2, 3];
  int[] b = a;
  return b; // Error: Scope variable `b` may not be returned.
}
« First   ‹ Prev
1 2 3 4 5