On Saturday, July 6, 2019 8:12:36 AM MDT berni via Digitalmars-d-learn 
> Now it's getting weird. Meanwhile I encountered, that take()
> sometimes consumes and sometimes not. Where can I learn, what is
> the reason behind this behavior? And how can I handle this?

take _always_ consumes the range that it's given. The problem is that some
types of ranges are implicitly saved when they're copied, whereas others
aren't, and when a range is implicitly saved when it's copied, you end up
with the copy being consumed. Dynamic arrays are implicitly saved when
they're copied, so the range you pass is saved, and the copy is consumed
instead of the original.

In generic code, you have to assume that once a range has been copied, you
can't use it anymore (just the copy) precisely because the semantics of
copying differ depending on the type of the range. You can only use a range
after copying it if you know what type of range you're dealing with and how
it behaves. So, you can rely on a dynamic array implicitly saving when it's
passed to take, but in generic code, you really shouldn't be using a range
again once you pass it to take, because what actually happens is dependent
on the type of the range.

In general what this means is that if you pass a range to a function, and
you then want to use the range again afterwards, you need to call save when
passing it to the function, and otherwise, you just assume that it's
consumed and don't use it again. You certainly don't pass it to a function
with the expectation of some elements being consumed and then continue to
use the rest of the range unless the function takes its argument by ref or
by pointer (which relatively few range-based functions do).

If you want a function that's guaranteed to not implicitly copy a range,
then it needs to accept the argument by ref or take a pointer to it. In the
case where a function doesn't have to do the work lazily, ref would work,
but in a case like take where you're returning a wrapper range, pointers
would be required. So, a version of take that didn't ever copy the range
it's given and thus never risked implicitly saving the range it was passed
would have to either take a pointer to it or take it by ref and then take
the address of the ref. In either case, the code using such a take would
then have to ensure that the original range didn't leave scope and get
destroyed before the take range was consumed, or the take range would then
refer to invalid memory.

- Jonathan M Davis

Reply via email to