writeln, alias this and dynamic arrays.

Steven Schveighoffer schveiguy at yahoo.com
Fri Nov 17 14:28:54 UTC 2017


On 11/17/17 8:13 AM, Adam D. Ruppe wrote:
> On Friday, 17 November 2017 at 06:57:16 UTC, Michael V. Franklin wrote:
>> Actually, it looks like it's this 4-year old bug. 
>> https://issues.dlang.org/show_bug.cgi?id=9506
> 
> Yeah, I suspected this is old and my comment in there still applies...
> 
> It passes the isInputRange test due to the implicit conversion of alias 
> this, and then writeln takes it and consumes it.

In doing some digging, I found that it actually calls the object version 
of formatValue. Which is specifically written to check if 
Object.toString has been overridden. If not, and it has a range 
interface, it treats it as a range.

So this is not a bug. By using alias this against a range, you have 
turned it into a range interface.

> One thing you might consider is making the alias this go to a getter 
> property instead of the method directly. Then operations on it will work 
> on a copy of the slice, and thus not consume the original one. But then 
> you can't do stuff like append do it directly either...

It doesn't work, I tried that. The reason is because, for arrays, 
popFront requires an lvalue (obviously), and so isInputRange for such a 
class is false.

It just prints the class name, like a normal class.

Ironically, alias this to a struct range type (like say filter), would 
be even more disastrous:

class C(R)
{
     R foo;
     R getFoo() { return foo; }
     alias getFoo this;
}


     auto f = [1, 2, 3, 4].filter!"a & 1";
     auto c = new C!(typeof(f));
     c.foo = f;
     writeln(c);// [1, 1, 1, 1, 1, 1,....

This is because C.getFoo.popFront works, even though it's an rvalue!

I don't think there's a way to figure out this situation and properly 
deal with it. My recommendation is basically, don't do this. It's not 
going to work like you expect.

> imo writeln should check for .save and use it. But meh I don't care to 
> personally bother fixing it and apparently nobody else does either.

.save isn't even properly implemented. It must return the same type as 
the original.

e.g.: static assert(!isForwardRange!C);

Jonathan has talked about this, and I agree with him. The only real way 
to fix forward ranges is to get rid of save, and make copyability the 
thing that makes a forward range a forward range. But that's not going 
to happen, too disruptive.

-Steve


More information about the Digitalmars-d-learn mailing list