Range Redesign: Copy Semantics

Steven Schveighoffer schveiguy at gmail.com
Wed Jan 24 16:18:11 UTC 2024


On Tuesday, 23 January 2024 at 18:21:47 UTC, Paul Backus wrote:
> On Tuesday, 23 January 2024 at 17:25:47 UTC, Steven 
> Schveighoffer wrote:
>> You are thinking about this the wrong way. Copyability becomes 
>> a trait of forward ranges, instead of input ranges. An input 
>> range would not be copyable, therefore code which takes input 
>> ranges would have to take them by reference, or the caller 
>> would have to ensure no other copies exist. But you can still 
>> take forward ranges this way.
>>
>> It is not hard to do -- you just remove the requirement for 
>> copying from `isInputRange`, and add it to `isForwardRange`.
>>
>> [...]
>>
>> The assertion is that if the range is copyable, it's now a 
>> forward range and copying is allowed. It is *on you* as the 
>> range developer to ensure copying does not work if it's an 
>> input range.
>
> The only problem with this is that what you are asking the 
> range developer to do is impossible. :)
>
> Consider what would happen if the user creates a pointer to a 
> non-copyable input range.  The pointer (a) is inherently 
> copyable and (b) implements the range interface (via implicit 
> dereferencing), but (c) the copies are not independent.
>
> (If your response to this is "ok, we'll just have 
> isForwardRange explicitly reject pointers", keep in mind that 
> this problem also applies to generic wrapper types that use 
> `alias this` or `opDispatch`. Are we really going to tell our 
> users that it's their fault if their code breaks because they 
> put a range pointer inside one of these types?)

Wouldn't that be on the person who wrapped the type? I wouldn't 
expect `RefCounted` to care that it's wrapping a range for 
instance.

We could explicitly forbid pointers and classes, because you 
can't obviously implement value semantics using those.

Indeed, since copying is defined by default, it's more onorous to 
properly create an input range. But so what? Can I tell you how 
many times people copy an input range without realizing the 
consequences today? At least if it's *explicit* that input ranges 
should not be copyable, then you will be more aware of the issue 
as the compiler complains about it.

>
> So, there has to be some other way of distinguishing forward 
> ranges from input ranges, beyond just checking whether they can 
> be copied. For example, by using `next()` for input ranges vs. 
> `head()`/`tail()` for forward ranges. (As [you pointed out 
> earlier][1], reusing the `empty`/`front`/`popFront` API would 
> not be a great idea.)

Yeah, I agree that if we change semantics, we have to change the 
naming, for the sake of all existing code. But the naming could 
be consistent between input/forward ranges as it is today. I'm 
not keen on the head/tail idea, as it seems way more clunky than 
I would like, and I'm not sold on the compiler making sure the `x 
= x.tail` operation is going to be efficient.

I don't know how bad it would be to deal with, it's not something 
I have put a lot of consideration into, but I had thought that 
this mechanism of preventing copying for things that shouldn't be 
used from multiple copies sounds like the proper way to prevent 
problems by default. Sometimes it's good to start with what is 
simple and correct, even if it's not what you want, and see if 
you can massage it into what makes sense.

-Steve


More information about the Digitalmars-d mailing list