Comment #0 by pro.mathias.lang — 2015-12-06T19:11:13Z
```
public struct IRange
{
int[] data;
public this(int[] d) { this.data = d; }
@disable this(this);
public int front () @property { return data[0]; }
public bool empty () @property { return !!data.length; }
public void popFront () { data = data[1..$]; }
}
void main ()
{
auto ir = IRange([1,2,3,4]);
foreach (v; ir)
{
}
}
```
Obviously it happens because it lowers to:
```
for (auto __r = ir; !__r.empty; __r.popFront()) {
auto v = __r.front;
}
```
Which means that input ranges which behaves as forward ranges when copied will have an implicit `.save` here. So fixing this bug could potentially affect a lot of code.
Related: https://issues.dlang.org/show_bug.cgi?id=4347 (note: I didn't use that issue because I don't propose we do an implicit `.save`, but rather we get rid of it in all circumstances).
Comment #1 by petar.p.kirov — 2016-06-30T16:15:32Z
I wonder if the solution could be as simple* as changing the lowering to:
for ( ; !ir.empty; ir.popFront()) {
auto v = __r.front;
}
That way foreach will work with truly input-only ranges (such as ones with @disabled this(this)) and the users could optionally insert a .save call, if needed, when they're working with forward ranges.
If this is an unacceptable breaking change, we could use the new lowering only when the postblit is disabled.
* I'm not very familiar the compiler internals, so I have no idea if this would be actually simple in practice.
Comment #2 by petar.p.kirov — 2016-06-30T16:17:04Z
I meant:
foreach (v; ir)
{
// user code
}
// =====v====
for ( ; !ir.empty; ir.popFront())
{
auto v = ir.front;
// user code
}
Comment #3 by issues.dlang — 2019-10-25T04:41:36Z
foreach specifically copies the range that it's given. If it didn't, then iterating over a dynamic array would consume it. It does unfortunately mean that whether a range is consumed or not depends on whether it's a reference type, pseudo-reference type, or value type, but that's a general problem with copying ranges. Either way, changing how foreach worked would break a lot of code.
So, in order for this to work, ranges which are non-copyable would have to be treated differently from other ranges when using foreach. However, considering that almost all range-based code copies ranges, I find it hard to believe that having a non-copyable range makes much sense. And if you specifically are looking for foreach to work, you should just be able to use opApply instead of making the object a range.
Comment #4 by issues.dlang — 2019-10-25T04:42:56Z
I would note that this issue has some similarities to issue #14478, but 14478 deals with elements which are non-copyable, whereas this deals with ranges which are non-copyable (though a range with non-copyable elements would also be non-copyable if the elements were contained directly in the range).
Comment #5 by johannespfau — 2019-11-10T09:11:22Z
> However, considering that almost all range-based code copies ranges, I find it hard to believe that having a non-copyable range makes much sense.
All implementations of Unique however work by disabling this(this), which means you can't foreach over unique ranges.
One place where this would be really idiomatic is this: I'd like to return a Range which maps d2sqlite3 result rows to the deserialized types. However, in SQLite you have to explicitly keep track of & finalize the statement which was use to generate the query. Ideally you could do just this:
foreach(value; db.query!Commit(/*sha =*/ "abcd") {}
and when the range goes out of scope it finalizes the statement. Because of foreachs implicit copying, you now have to use reference counting instead...
Comment #6 by robert.schadek — 2024-12-13T18:46:02Z