On Friday, 13 July 2012 at 22:27:01 UTC, Jonathan M Davis wrote:
On Friday, July 13, 2012 14:11:44 monarch_dodra wrote:
But I still feel that the internal call to opSlice/copy is
really
a just trap disguised as a safety net...
Yeah. Kind of.
The thing is that there's no way around the fact that structs
and arrays are
effectively value types as far as ranges go, whereas classes
are reference
types. If you want them to be copied consistently, you need to
use save. If
you don't care whether a copy is made or not, then you don't
worry about it.
But foreach has to be treated specially with ranges anyway,
because of
strings.
foreach(e; str) {}
is going to iterate over char, not dchar, whereas strings are
ranges of dchar.
So, if you iterate over a range generically, you need to do
foreach(ElementType!R e; range) {}
So, you have to be careful with foreach already, and having it
_not_ save but
having it copy the range automatically (and therefore
implicitly saving for
non-reference type forward ranges) is completely consistent
with how it works
with passing ranges to functions, so in that sense, it really
isn't all that
bad. If anything, things are _more_ consistent that way.
The only way to really solve the save problem would be to
disallow forward
ranges which were reference types (which you could mostly do by
disallowing
classes, but I don' think that there's any real way to
statically check
whether a struct is a reference type or not). Then save
wouldn't be needed,
and ranges would all function the same as long as no one made
structs which
were reference types into ranges. But that would also cut off
some useful
idioms (sometimes, you _want_ a range to be consumed by a
function rather than
being implicitly copied - which is why I added
std.range.RefRange for 2.060).
So, I don't know of any way to really fix the problem. You need
save to make it
possible to copy reference types, and there's no way to
reasonably avoid
having to deal with both value type and reference type ranges.
So, we just deal with it. Unfortunately, far too often, the
result of that is
that value type ranges work properly and reference type ranges
don't, since
range-based functions are usually tested with just value types
ranges, but
improved testing fixes that, and that's not all that hard to do.
- Jonathan M Davis
Very good points. I appreciate your answer.
I guess I should consider the "copy" the standard byproduct of
"passing to foreach" => I should _also_ remember to save before
passing a range to a function.