Open nightkr opened 1 year ago
What is it about RangeFrom
that it should have this, but other infinite things (say, Repeat
) shouldn't? Or would this set a precedent that other things would get the method too?
Personally, my instinct is that if it's worth having, then having a trait for it would be nice, so that Enumerate<Repeat<_>>
would also have it, for example.
That said, making a crate to wrap .next().unwrap_unchecked()
might also be sufficient.
nit: advance
makes me think of the existing advance_by
, so I'm not convinced it's a good name.
What is it about RangeFrom that it should have this, but other infinite things (say, Repeat) shouldn't? Or would this set a precedent that other things would get the method too?
IMO Repeat
is already covered for this by Clone
, so the primary use-case would be in composition.
Personally, my instinct is that if it's worth having, then having a trait for it would be nice, so that Enumerate<Repeat<_>> would also have it, for example.
I'd like to see a trait, but I don't think it's possible in a way that's both clean and correct by construction. There are three obvious ways this could be done:
I personally don't like any of these. Option 1 leaves the door wide open for implementations to diverge. Option 2 isn't much better than unwrap()
, it doesn't guarantee anything by its design. If we want it to be fast and use unwrap_unchecked()
then it'd have to be an unsafe trait
which just feels wrong. Option 3 makes it completely impossible for a type to be both always an Iterator
and selectively an InfiniteOperator
.
That said, making a crate to wrap .next().unwrap_unchecked() might also be sufficient.
Yeah.. this comes down to the correct-by-construction question again. In practice I don't see RangeFrom
ever violating this, but I'd rather have it be a promise made by RangeFrom
itself, rather than an external crate making the promise on its behalf.
nit: advance makes me think of the existing advance_by, so I'm not convinced it's a good name.
Me neither, consider it a placeholder.
Another alternative would be to redefine Iterator
as a special case of a generic supertrait, but that makes the compatibility story pretty hairy:
When we'll have pattern types, and if we'll be able to refine trait methods using pattern types, we will be able to declare fn next(&mut self) -> Option<Self::Item> is Option::Some
or whatever syntax it will be.
Oh, I didn't know those were in the works. Is there an RFC somewhere?
One nice thing about an InfiniteIterator
is that it could also enable things like a zip_infinite
that wouldn't have some of the optimization/bounds complications that apply to the existing zip
.
Such a trait would also open up possibilities like a to-array method that doesn't have to worry about handling the "iterator is too short" cases.
Proposal
Problem statement
RangeFrom
is almost a convenient way to count and index things, but its adherence toIterator
makesnext
fallible, even if there is no actual stop condition (as opposed to fullRange
, which has a defined endpoint).This is effectively equivalent to the venerable
i++
.Motivating examples or use cases
The
unwrap
is required here to avoid bubbling up the impossible error, but will still set off alarms where panics are forbidden or discouraged (for example, this gets in the way of#[deny(clippy::unwrap_used)]
).Solution sketch
RangeFrom::next()
's implementation is already infallible and can be moved intoRangeFrom::advance()
(name pending).next()
can then be redefined asSome(self.advance())
.Alternatives
Use
Iterator::enumerate()
(AKAIterator::zip(0..)
)This should be preferred where possible, but doesn't solve cases where you only want to count some subset of values (without filtering out the others entirely).
Increment manually
The same effect can be accomplished by incrementing the integer manually, for example:
However, the extra statement feels awkward, and breaks up the otherwise nice expression-oriented code into a read-modify-return sequence.
Publish to crates.io
This would be a possible (if a bit ugly) option, but it would have to wrap the fallible
RangeFrom::next()
(or reimplement all of the machinery from scratch).Define an
InfiniteIterator
traitConceptually this makes sense, but it interacts awkwardly with Rust's type system. Ideally,
Iterator::next()
would be implied byInfiniteIterator::advance()
, but that makes combinators impossible to implement neatly.Links and related work
This is effectively equivalent to the venerable
i++
operator.What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution: