opNext: Simplified ranges
Steven Schveighoffer
schveiguy at gmail.com
Sat Nov 5 23:32:51 UTC 2022
On 11/5/22 2:08 PM, Paul Backus wrote:
> On Saturday, 5 November 2022 at 15:17:42 UTC, Steven Schveighoffer wrote:
>> This can be solved by a wrapper:
>>
>> ```d
>> struct RefOf(T)
>> {
>> private T *_val;
>> ref T _get() { return *_val; }
>> alias _get this;
>> }
>>
>> auto refOf(T)(return ref T val) { return RefOf!T(&val); }
>>
>> foreach(elem; range.refOf) // now doesn't make a copy.
>> ```
>
> You'd think, but if you actually try this, it turns out it doesn't work:
> the compiler is "smart" enough to see through the `alias this`, and
> makes a copy of `range.refOf._get` (the actual range object) instead of
> `range.refOf` (the wrapper).
It's not "smart", just picky.
>
> So, for example, this usage code prints `[1, 2, 3]`, demonstrating that
> the range is *not* consumed by the `foreach` loop:
>
> ```d
> void main()
> {
> import std.range, std.stdio;
>
> auto r = only(1, 2, 3);
> foreach (e; refOf(r)) {}
> writeln(r);
> }
> ```
>
> In the `-vcg-ast` output, you can see the call to `_get` inserted by the
> compiler:
>
> ```d
> void main()
> {
> import std.range;
> import std.stdio;
> OnlyResult!(int, int, int) r = only(1, 2, 3);
> {
> OnlyResult!(int, int, int) __r47 = refOf(r)._get().opSlice();
> for (; !__r47.empty(); __r47.popFront())
> {
> int e = __r47.front();
> }
> }
> writeln(r);
> return 0;
> }
> ```
What is happening is the compiler is not using duck typing exactly, and
it also has some pretty puzzling logic.
1. If it has the ability to slice, it will *always do this at least
once*. See the bug reported long ago:
https://issues.dlang.org/show_bug.cgi?id=14619
2. The definition of a range is that it contains a `front` *member*.
This cannot be via `alias this`.
So this works:
```d
struct RefOf(T)
{
private T *_val;
auto front() { return _get.front; }
RefOf opSlice() { return this; }
ref T _get() { return *_val; }
alias _get this;
}
```
Take away from that what you will.
(also I forgot there actually is something called `std.range.refRange`
which *is supposed to do this*, but apparently still broken).
-Steve
More information about the Digitalmars-d
mailing list