Thread overview
input range with non copyable element
Aug 08, 2021
vit
Aug 09, 2021
Mathias LANG
Aug 09, 2021
vit
August 08, 2021

Hello, is there reason why elements of input range must be copyable?

For example this example works and copy ctor is never called:

import std.algorithm : map;
import std.range;

struct Foo{
	int i;

    this(scope ref typeof(this) rhs)pure nothrow @safe @nogc{
    	//this.i = rhs.i;
        assert(0, "no copy");
    }
	@disable this(scope const ref typeof(this) rhs)pure nothrow @safe @nogc;
}


void main(){
    Foo[] foos = [Foo(1), Foo(2), Foo(3)];

    //this work (copy ctor is never called):
    {
        auto tmp = foos
            .map!((ref foo) => foo.i)
            .array;
    }
}

This doesn't work:


void main(){
    const(Foo)[] foos = [Foo(1), Foo(2), Foo(3)];

    //error:
    {
        auto tmp = foos
            .map!((ref foo) => foo.i)
            .array;
    }
}

Source of my problem is in isInputRange:

import std.range;
import std.traits : ReturnType;

alias R = const(Foo)[];
//enum bool isInputRange(R) =
static assert(is(typeof(R.init) == R));								///OK
static assert(is(ReturnType!((R r) => r.empty) == bool));			///OK
static assert(is(typeof((return ref R r) => r.front)));				///ERROR, copy result of front
static assert(!is(ReturnType!((R r) => r.front) == void));			///OK
static assert(is(typeof((R r) => r.popFront)));						///OK

Is it possible to make lambda return auto ref?

August 09, 2021

On Sunday, 8 August 2021 at 18:36:02 UTC, vit wrote:

>

Hello, is there reason why elements of input range must be copyable?

By design, not that I can think of. But it is assumed all over the place, unfortunately. You can make your front method return by ref, but you're still going to get bitten as soon as you do any std.algorithm-based operation, as storage classes are not inferred (https://issues.dlang.org/show_bug.cgi?id=9423) and things get passed by value to your delegates by default. The problem can also show up when you foreach over a range (https://issues.dlang.org/show_bug.cgi?id=15413). And finally, std.algorithm need to support it.

So currently you'll have to jump through a lot of hops to get it to work. It'll be much easier to use your own map & co to get the job done for the time being. Hopefully at some point in the near future that won't be needed anymore.

August 09, 2021

On Monday, 9 August 2021 at 02:47:40 UTC, Mathias LANG wrote:

>

On Sunday, 8 August 2021 at 18:36:02 UTC, vit wrote:

>

Hello, is there reason why elements of input range must be copyable?

By design, not that I can think of. But it is assumed all over the place, unfortunately. You can make your front method return by ref, but you're still going to get bitten as soon as you do any std.algorithm-based operation, as storage classes are not inferred (https://issues.dlang.org/show_bug.cgi?id=9423) and things get passed by value to your delegates by default. The problem can also show up when you foreach over a range (https://issues.dlang.org/show_bug.cgi?id=15413). And finally, std.algorithm need to support it.

So currently you'll have to jump through a lot of hops to get it to work. It'll be much easier to use your own map & co to get the job done for the time being. Hopefully at some point in the near future that won't be needed anymore.

Look like I am not the first who has this problem: https://issues.dlang.org/show_bug.cgi?id=14478

I copy part of std.algorithm I use and change isInputRange to:

enum bool isInputRange(R) = true
    && is(typeof(R.init) == R)									///no change
    && is(ReturnType!((R r) => r.empty) == bool)				///no change
	&& is(typeof((return ref R r) => (auto ref x){}(r.front)))	///Before: , is(typeof((return ref R r) => r.front))
    && !is(ReturnType!((R r) => r.front) == void)				///no change
    && is(typeof((R r) => r.popFront));							///no change