Skip to content

Commit ebf79b5

Browse files
committed
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
1 parent c4f2577 commit ebf79b5

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed

library/core/src/iter/adapters/take.rs

+57
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,60 @@ impl<I: Iterator + TrustedRandomAccess> SpecTake for Take<I> {
312312
}
313313
}
314314
}
315+
316+
#[stable(feature = "exact_size_take_repeat", since = "CURRENT_RUSTC_VERSION")]
317+
impl<T: Clone> DoubleEndedIterator for Take<crate::iter::Repeat<T>> {
318+
#[inline]
319+
fn next_back(&mut self) -> Option<Self::Item> {
320+
self.next()
321+
}
322+
323+
#[inline]
324+
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
325+
self.nth(n)
326+
}
327+
328+
#[inline]
329+
fn try_rfold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
330+
where
331+
Self: Sized,
332+
Fold: FnMut(Acc, Self::Item) -> R,
333+
R: Try<Output = Acc>,
334+
{
335+
self.try_fold(init, fold)
336+
}
337+
338+
#[inline]
339+
fn rfold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
340+
where
341+
Self: Sized,
342+
Fold: FnMut(Acc, Self::Item) -> Acc,
343+
{
344+
self.fold(init, fold)
345+
}
346+
347+
#[inline]
348+
#[rustc_inherit_overflow_checks]
349+
fn advance_back_by(&mut self, n: usize) -> Result<(), NonZeroUsize> {
350+
self.advance_by(n)
351+
}
352+
}
353+
354+
// Note: It may be tempting to impl DoubleEndedIterator for Take<RepeatWith>.
355+
// One must fight that temptation since such implementation wouldn’t be correct
356+
// because we have no way to return value of nth invocation of repeater followed
357+
// by n-1st without remembering all results.
358+
359+
#[stable(feature = "exact_size_take_repeat", since = "CURRENT_RUSTC_VERSION")]
360+
impl<T: Clone> ExactSizeIterator for Take<crate::iter::Repeat<T>> {
361+
fn len(&self) -> usize {
362+
self.n
363+
}
364+
}
365+
366+
#[stable(feature = "exact_size_take_repeat", since = "CURRENT_RUSTC_VERSION")]
367+
impl<F: FnMut() -> A, A> ExactSizeIterator for Take<crate::iter::RepeatWith<F>> {
368+
fn len(&self) -> usize {
369+
self.n
370+
}
371+
}

library/core/tests/iter/adapters/take.rs

+89
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,92 @@ fn test_byref_take_consumed_items() {
167167
assert_eq!(count, 70);
168168
assert_eq!(inner, 90..90);
169169
}
170+
171+
#[test]
172+
fn test_exact_size_take_repeat() {
173+
let mut iter = core::iter::repeat(42).take(40);
174+
assert_eq!((40, Some(40)), iter.size_hint());
175+
assert_eq!(40, iter.len());
176+
177+
assert_eq!(Some(42), iter.next());
178+
assert_eq!((39, Some(39)), iter.size_hint());
179+
assert_eq!(39, iter.len());
180+
181+
assert_eq!(Some(42), iter.next_back());
182+
assert_eq!((38, Some(38)), iter.size_hint());
183+
assert_eq!(38, iter.len());
184+
185+
assert_eq!(Some(42), iter.nth(3));
186+
assert_eq!((34, Some(34)), iter.size_hint());
187+
assert_eq!(34, iter.len());
188+
189+
assert_eq!(Some(42), iter.nth_back(3));
190+
assert_eq!((30, Some(30)), iter.size_hint());
191+
assert_eq!(30, iter.len());
192+
193+
assert_eq!(Ok(()), iter.advance_by(10));
194+
assert_eq!((20, Some(20)), iter.size_hint());
195+
assert_eq!(20, iter.len());
196+
197+
assert_eq!(Ok(()), iter.advance_back_by(10));
198+
assert_eq!((10, Some(10)), iter.size_hint());
199+
assert_eq!(10, iter.len());
200+
}
201+
202+
#[test]
203+
fn test_exact_size_take_repeat_with() {
204+
let mut counter = 0;
205+
let mut iter = core::iter::repeat_with(move || {
206+
counter += 1;
207+
counter
208+
}).take(40);
209+
assert_eq!((40, Some(40)), iter.size_hint());
210+
assert_eq!(40, iter.len());
211+
212+
assert_eq!(Some(1), iter.next());
213+
assert_eq!((39, Some(39)), iter.size_hint());
214+
assert_eq!(39, iter.len());
215+
216+
assert_eq!(Some(5), iter.nth(3));
217+
assert_eq!((35, Some(35)), iter.size_hint());
218+
assert_eq!(35, iter.len());
219+
220+
assert_eq!(Ok(()), iter.advance_by(10));
221+
assert_eq!((25, Some(25)), iter.size_hint());
222+
assert_eq!(25, iter.len());
223+
224+
assert_eq!(Some(16), iter.next());
225+
assert_eq!((24, Some(24)), iter.size_hint());
226+
assert_eq!(24, iter.len());
227+
}
228+
229+
// This is https://github.com/rust-lang/rust/issues/104729 with all uses of
230+
// repeat(0) were replaced by repeat(0).take(20).
231+
#[test]
232+
fn test_reverse_on_zip() {
233+
let vec_1 = [1; 10];
234+
235+
let zipped_iter = vec_1.iter().copied().zip(core::iter::repeat(0).take(20));
236+
237+
// Forward
238+
for (one, zero) in zipped_iter {
239+
assert_eq!((1, 0), (one, zero));
240+
}
241+
242+
let rev_vec_iter = vec_1.iter().rev();
243+
let rev_repeat_iter = std::iter::repeat(0).take(20).rev();
244+
245+
// Manual reversed zip
246+
let rev_zipped_iter = rev_vec_iter.zip(rev_repeat_iter);
247+
248+
for (&one, zero) in rev_zipped_iter {
249+
assert_eq!((1, 0), (one, zero));
250+
}
251+
252+
let zipped_iter = vec_1.iter().zip(core::iter::repeat(0).take(20));
253+
254+
// Cannot call rev here for automatic reversed zip constuction
255+
for (&one, zero) in zipped_iter.rev() {
256+
assert_eq!((1, 0), (one, zero));
257+
}
258+
}

0 commit comments

Comments
 (0)