Skip to content

Commit 5849a7e

Browse files
committed
Auto merge of rust-lang#77594 - timvermeulen:chain_advance_by, r=scottmcm
Implement advance_by, advance_back_by for iter::Chain Part of rust-lang#77404. This PR does two things: - implement `Chain::advance[_back]_by` in terms of `advance[_back]_by` on `self.a` and `advance[_back]_by` on `self.b` - change `Chain::nth[_back]` to use `advance[_back]_by` on `self.a` and `nth[_back]` on `self.b` This ensures that `Chain::nth` can take advantage of an efficient `nth` implementation on the second iterator, in case it doesn't implement `advance_by`. cc `@scottmcm` in case you want to review this
2 parents 5ded394 + 1d27a50 commit 5849a7e

File tree

2 files changed

+167
-12
lines changed

2 files changed

+167
-12
lines changed

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

+64-12
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,42 @@ where
126126
}
127127

128128
#[inline]
129-
fn nth(&mut self, mut n: usize) -> Option<A::Item> {
129+
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
130+
let mut rem = n;
131+
130132
if let Some(ref mut a) = self.a {
131-
while let Some(x) = a.next() {
132-
if n == 0 {
133-
return Some(x);
134-
}
135-
n -= 1;
133+
match a.advance_by(rem) {
134+
Ok(()) => return Ok(()),
135+
Err(k) => rem -= k,
136136
}
137137
self.a = None;
138138
}
139+
140+
if let Some(ref mut b) = self.b {
141+
match b.advance_by(rem) {
142+
Ok(()) => return Ok(()),
143+
Err(k) => rem -= k,
144+
}
145+
// we don't fuse the second iterator
146+
}
147+
148+
if rem == 0 { Ok(()) } else { Err(n - rem) }
149+
}
150+
151+
#[inline]
152+
fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
153+
if let Some(ref mut a) = self.a {
154+
match a.advance_by(n) {
155+
Ok(()) => match a.next() {
156+
None => n = 0,
157+
x => return x,
158+
},
159+
Err(k) => n -= k,
160+
}
161+
162+
self.a = None;
163+
}
164+
139165
maybe!(self.b.nth(n))
140166
}
141167

@@ -202,16 +228,42 @@ where
202228
}
203229

204230
#[inline]
205-
fn nth_back(&mut self, mut n: usize) -> Option<A::Item> {
231+
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
232+
let mut rem = n;
233+
206234
if let Some(ref mut b) = self.b {
207-
while let Some(x) = b.next_back() {
208-
if n == 0 {
209-
return Some(x);
210-
}
211-
n -= 1;
235+
match b.advance_back_by(rem) {
236+
Ok(()) => return Ok(()),
237+
Err(k) => rem -= k,
212238
}
213239
self.b = None;
214240
}
241+
242+
if let Some(ref mut a) = self.a {
243+
match a.advance_back_by(rem) {
244+
Ok(()) => return Ok(()),
245+
Err(k) => rem -= k,
246+
}
247+
// we don't fuse the second iterator
248+
}
249+
250+
if rem == 0 { Ok(()) } else { Err(n - rem) }
251+
}
252+
253+
#[inline]
254+
fn nth_back(&mut self, mut n: usize) -> Option<Self::Item> {
255+
if let Some(ref mut b) = self.b {
256+
match b.advance_back_by(n) {
257+
Ok(()) => match b.next_back() {
258+
None => n = 0,
259+
x => return x,
260+
},
261+
Err(k) => n -= k,
262+
}
263+
264+
self.b = None;
265+
}
266+
215267
maybe!(self.a.nth_back(n))
216268
}
217269

library/core/tests/iter.rs

+103
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,43 @@ use core::cell::Cell;
44
use core::convert::TryFrom;
55
use core::iter::*;
66

7+
/// An iterator wrapper that panics whenever `next` or `next_back` is called
8+
/// after `None` has been returned.
9+
struct Unfuse<I> {
10+
iter: I,
11+
exhausted: bool,
12+
}
13+
14+
fn unfuse<I: IntoIterator>(iter: I) -> Unfuse<I::IntoIter> {
15+
Unfuse { iter: iter.into_iter(), exhausted: false }
16+
}
17+
18+
impl<I> Iterator for Unfuse<I>
19+
where
20+
I: Iterator,
21+
{
22+
type Item = I::Item;
23+
24+
fn next(&mut self) -> Option<Self::Item> {
25+
assert!(!self.exhausted);
26+
let next = self.iter.next();
27+
self.exhausted = next.is_none();
28+
next
29+
}
30+
}
31+
32+
impl<I> DoubleEndedIterator for Unfuse<I>
33+
where
34+
I: DoubleEndedIterator,
35+
{
36+
fn next_back(&mut self) -> Option<Self::Item> {
37+
assert!(!self.exhausted);
38+
let next = self.iter.next_back();
39+
self.exhausted = next.is_none();
40+
next
41+
}
42+
}
43+
744
#[test]
845
fn test_lt() {
946
let empty: [isize; 0] = [];
@@ -142,6 +179,72 @@ fn test_iterator_chain() {
142179
assert_eq!(i, expected.len());
143180
}
144181

182+
#[test]
183+
fn test_iterator_chain_advance_by() {
184+
fn test_chain(xs: &[i32], ys: &[i32]) {
185+
let len = xs.len() + ys.len();
186+
187+
for i in 0..xs.len() {
188+
let mut iter = unfuse(xs).chain(unfuse(ys));
189+
iter.advance_by(i).unwrap();
190+
assert_eq!(iter.next(), Some(&xs[i]));
191+
assert_eq!(iter.advance_by(100), Err(len - i - 1));
192+
}
193+
194+
for i in 0..ys.len() {
195+
let mut iter = unfuse(xs).chain(unfuse(ys));
196+
iter.advance_by(xs.len() + i).unwrap();
197+
assert_eq!(iter.next(), Some(&ys[i]));
198+
assert_eq!(iter.advance_by(100), Err(ys.len() - i - 1));
199+
}
200+
201+
let mut iter = xs.iter().chain(ys);
202+
iter.advance_by(len).unwrap();
203+
assert_eq!(iter.next(), None);
204+
205+
let mut iter = xs.iter().chain(ys);
206+
assert_eq!(iter.advance_by(len + 1), Err(len));
207+
}
208+
209+
test_chain(&[], &[]);
210+
test_chain(&[], &[0, 1, 2, 3, 4, 5]);
211+
test_chain(&[0, 1, 2, 3, 4, 5], &[]);
212+
test_chain(&[0, 1, 2, 3, 4, 5], &[30, 40, 50, 60]);
213+
}
214+
215+
#[test]
216+
fn test_iterator_chain_advance_back_by() {
217+
fn test_chain(xs: &[i32], ys: &[i32]) {
218+
let len = xs.len() + ys.len();
219+
220+
for i in 0..ys.len() {
221+
let mut iter = unfuse(xs).chain(unfuse(ys));
222+
iter.advance_back_by(i).unwrap();
223+
assert_eq!(iter.next_back(), Some(&ys[ys.len() - i - 1]));
224+
assert_eq!(iter.advance_back_by(100), Err(len - i - 1));
225+
}
226+
227+
for i in 0..xs.len() {
228+
let mut iter = unfuse(xs).chain(unfuse(ys));
229+
iter.advance_back_by(ys.len() + i).unwrap();
230+
assert_eq!(iter.next_back(), Some(&xs[xs.len() - i - 1]));
231+
assert_eq!(iter.advance_back_by(100), Err(xs.len() - i - 1));
232+
}
233+
234+
let mut iter = xs.iter().chain(ys);
235+
iter.advance_back_by(len).unwrap();
236+
assert_eq!(iter.next_back(), None);
237+
238+
let mut iter = xs.iter().chain(ys);
239+
assert_eq!(iter.advance_back_by(len + 1), Err(len));
240+
}
241+
242+
test_chain(&[], &[]);
243+
test_chain(&[], &[0, 1, 2, 3, 4, 5]);
244+
test_chain(&[0, 1, 2, 3, 4, 5], &[]);
245+
test_chain(&[0, 1, 2, 3, 4, 5], &[30, 40, 50, 60]);
246+
}
247+
145248
#[test]
146249
fn test_iterator_chain_nth() {
147250
let xs = [0, 1, 2, 3, 4, 5];

0 commit comments

Comments
 (0)