On 8/25/21 12:46 PM, Joseph Rushton Wakeling wrote:
On Wednesday, 25 August 2021 at 10:59:44 UTC, Steven Schveighoffer wrote:
structs still provide a mechanism (postblit/copy ctor) to properly
save a forward range when copying, even if the guts need copying
(unlike classes). In general, I think it was a mistake to use `.save`
as the mechanism, as generally `.save` is equivalent to copying, so
nobody does it, and code works fine for most ranges.
Consider a struct whose internal fields are just a pointer to its "true"
internal state. Does one have any right to assume that the
postblit/copy ctor would necessarily deep-copy that?
In a world where copyability means it's a forward range? Yes. We aren't
in that world, it's a hypothetical "if we could go back and redesign".
If that struct implements a forward range, though, and that pointed-to
state is mutated by iteration of the range, then it would be reasonable
to assume that the `save` method MUST deep-copy it, because otherwise
the forward-range property would not be respected.
With that in mind, I am not sure it's reasonable to assume that just
because a struct implements a forward-range API, that copying the struct
instance is necessarily the same as saving the range.
Technically this is true. In practice, it rarely happens. The flaw of
`save` isn't that it's an unsound API, the flaw is that people get away
with just copying, and it works 99.9% of the time. So code is simply
untested with ranges where `save` is important.
Indeed, IIRC quite a few Phobos library functions program defensively
against that difference by taking a `.save` copy of their input before
iterating over it.
I'd be willing to bet $10 there is a function in phobos right now, that
takes forward ranges, and forgets to call `save` when iterating with
foreach. It's just so easy to do, and works with most ranges in existence.
What should have happened is that input-only ranges should not have
been copyable, and copying should have been the save mechanism. Then
it becomes way way more obvious what is happening. Yes, this means
forgoing classes as ranges.
I think there's a benefit of a method whose definition is explicitly "If
you call this, you will get a copy of the range which will replay
exactly the same results when iterating over it". Just because the
meaning of "copy" can be ambiguous, whereas a promise about how
iteration can be used is not.
The idea is to make the meaning of a range copy not ambiguous.
-Steve