foreach() behavior on ranges

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Aug 25 19:51:36 UTC 2021


On Wed, Aug 25, 2021 at 04:46:54PM +0000, Joseph Rushton Wakeling via Digitalmars-d-learn 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?
[...]
> 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.
[...]

What I understand from what Andrei has said in the past, is that a range
is merely a "view" into some underlying storage; it is not responsible
for the contents of that storage.  My interpretation of this is that
.save will only save the *position* of the range, but it will not save
the contents it points to, so it will not (should not) deep-copy.

However, if the range is implemented by a struct that contains a
reference to its iteration state, then yes, to satisfy the definition of
.save it should deep-copy this state.


> 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.
[...]

Andrei has mentioned before that in retrospect, .save was a design
mistake.  The difference between an input range and a forward range
should have been keyed on whether the range type has reference semantics
(input range) or by-value semantics (forward range).  But for various
reasons, including the state of the language at the time the range API
was designed, the .save route was chosen, and we're stuck with it unless
Phobos 2.0 comes into existence.

Either way, though, the semantics of a forward range pretty much
dictates that whatever type a range has, if it claims to be a forward
range then .save must preserve whatever iteration state it has at that
point in time. If this requires deep-copying some state referenced from
a struct, then that's what it takes to satisfy the API.  This may take
the form of a .save method that copies state, or a copy ctor that does
the same, or simply storing iteration state as PODs in the range struct
so that copying the struct equates to preserving the iteration state.


T

-- 
Why waste time reinventing the wheel, when you could be reinventing the engine? -- Damian Conway


More information about the Digitalmars-d-learn mailing list