-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ACP: Uplift iter::repeat_n
from itertools
#120
Comments
Smaller hammer: impl<T> ExactSizeIterator for Take<Repeat<T>> where T: Clone {}
This could be solved with specialization on |
It's also a specialization observable by users, which is unfortunate. |
By the way, I was unaware of repeat_n and ended up implementing
I think this is unnecessarily strict view of observable behaviour especially since documentation doesn’t state whether repeat clones every value or not. I think the bigger issue is to consider timeline of specialisation becoming. |
We discussed this in today's libs-api meeting. Seems good; it's clearer and a slight optimization. Feel free to open a tracking issue and open a PR to rust-lang/rust to add it as an unstable feature. |
Tracking issue: rust-lang/rust#104434 I'll make a PR to remove the EDIT: rust-lang/rust#120045 |
…imulacrum Un-hide `iter::repeat_n` ACP accepted in rust-lang/libs-team#120 (comment)
…imulacrum Un-hide `iter::repeat_n` ACP accepted in rust-lang/libs-team#120 (comment)
Rollup merge of rust-lang#120045 - scottmcm:unhide-repeat-n, r=Mark-Simulacrum Un-hide `iter::repeat_n` ACP accepted in rust-lang/libs-team#120 (comment)
Un-hide `iter::repeat_n` ACP accepted in rust-lang/libs-team#120 (comment)
Un-hide `iter::repeat_n` ACP accepted in rust-lang/libs-team#120 (comment)
…lnay Implement DoubleEnded and ExactSize for Take<Repeat> and Take<RepeatWith> Repeat iterator always returns the same element and behaves the same way backwards and forwards. Take iterator can trivially implement backwards iteration over Repeat inner iterator by simply doing forwards iteration. DoubleEndedIterator is not currently implemented for Take<Repeat<T>> because Repeat doesn’t implement ExactSizeIterator which is a required bound on DEI implementation for Take. Similarly, since Repeat is an infinite iterator which never stops, Take can trivially know how many elements it’s going to return. This allows implementing ExactSizeIterator on Take<Repeat<T>>. While at it, observe that ExactSizeIterator can also be implemented for Take<RepeatWhile<F>> so add that implementation too. Since in contrast to Repeat, RepeatWhile doesn’t guarante to always return the same value, DoubleEndedIterator isn’t implemented. Those changes render core::iter::repeat_n somewhat redundant. Issue: rust-lang#104434 Issue: rust-lang#104729 - [ ] ACP: rust-lang/libs-team#120 (this is actually ACP for repeat_n but this is nearly the same functionality so hijacking it so both approaches can be discussed in one place)
…lnay Implement DoubleEnded and ExactSize for Take<Repeat> and Take<RepeatWith> Repeat iterator always returns the same element and behaves the same way backwards and forwards. Take iterator can trivially implement backwards iteration over Repeat inner iterator by simply doing forwards iteration. DoubleEndedIterator is not currently implemented for Take<Repeat<T>> because Repeat doesn’t implement ExactSizeIterator which is a required bound on DEI implementation for Take. Similarly, since Repeat is an infinite iterator which never stops, Take can trivially know how many elements it’s going to return. This allows implementing ExactSizeIterator on Take<Repeat<T>>. While at it, observe that ExactSizeIterator can also be implemented for Take<RepeatWhile<F>> so add that implementation too. Since in contrast to Repeat, RepeatWhile doesn’t guarante to always return the same value, DoubleEndedIterator isn’t implemented. Those changes render core::iter::repeat_n somewhat redundant. Issue: rust-lang#104434 Issue: rust-lang#104729 - [ ] ACP: rust-lang/libs-team#120 (this is actually ACP for repeat_n but this is nearly the same functionality so hijacking it so both approaches can be discussed in one place)
Proposal
Problem statement
Add the
repeat_n<T>(value: T, n: usize) -> RepeatN<T>
function and iterator type toiter
.This exists as
itertools::repeat_n
, but can easily be uplifted to core+std, like howiter::repeat_with
has obviateditertools::repeat_call
.(This is a function, not a trait method, so doesn't have the resolution issues that make this hard for things like
intersperse
.)Motivation, use-cases
The obvious alternative here is
iter::repeat(value).take(n)
. But there's two gotchas with that approach:It has to always clone
value
, never move itIf it's a string, for example, that buffer can never be reused (other than in specializations) since it needs to always keep the value around for the potentially-next iteration. (And even if it somehow knew it was the last element needed, it still can't move it in non-consuming calls as it'd make a follow-up
.next()
call UB, and the Drop would double-free.)Vec
has the secretfrom_elem
forvec!
to use to overcome this problem, as well as a bunch of extra internal stuff likeExtendElement
for other places that want this functionality.But other containers don't have this. For example,
will do 10 clones (instead of the 9 that are really needed) and there's no nice way to avoid that.
Even
VecDeque::resize
didn't bother doing better here, as it just calls.resize_with(n, || value.clone())
, which will also do the extra clone.Thus one would need to do some long dance like
and that's enough of a pain that it's almost certainly not happening.
It would be nice to be able to just do
iter::repeat_n(my_string, n).collect()
and have that work great in containers, rather than needing them all to addfrom_elem
-like methods or recreate extend specialization infrastructure for all of them like Vec is doing.It's not
ExactSizeIterator
.While that does have a perfect
size_hint
,iter::Repeat<_>: !ExactSizeIterator
(and must not be) and thus theTake<…>
isn't either:https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=74ea40842f56f87224a4bb2411967ec6
That could be made to work, but the "obvious" way would need both an
InfiniteIterator
trait and some#[marker]
traits to express the bound, which combined make a pretty big hammer.Whereas
RepeatN<_>
is non-infinite -- limited by theusize
-- so is naturallyExactSizeIterator
. Thus even for things likerepeat(0).take(n)
where buffer reuse is irrelevant, this is still useful. (Such as in-> impl ExactSizeIterator<Item = …>
APIs.)Solution sketches
So long as
clone
ing in this isn't considered a side effect, it can be made to work nicely. That should be uncontroversial, as the existingrepeat
doesn't either -- for exampleiter::repeat(NoisyClone).nth(10)
only clones once: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b8455030a83615d6b4a1b92c25d65571A straight-forward and natural implementation even allows LLVM to unify the "clone" and "move the last time" branches for trivial-to-clone types even if they're not
Copy
-- with no specialneeds_drop
checks or specialization: https://rust.godbolt.org/z/drjzsdfoeLinks and related work
As mentioned, https://docs.rs/itertools/latest/itertools/fn.repeat_n.html
The suffix
_n
for "counted" versions of methods is used in Elements of Programming and common in C++ functions likecopy_n
.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 in its weekly meeting. You should receive feedback within a week or two.
The text was updated successfully, but these errors were encountered: