Skip to content

Commit 9cf2438

Browse files
authored
Rollup merge of #79479 - camelid:intersperse, r=m-ou-se
Add `Iterator::intersperse` This is a rebase of #75784. I'm hoping to push this past the finish line! cc `@jonas-schievink`
2 parents 242a252 + 40bbb7f commit 9cf2438

File tree

8 files changed

+189
-2
lines changed

8 files changed

+189
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use super::Peekable;
2+
3+
/// An iterator adapter that places a separator between all elements.
4+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
5+
#[derive(Debug, Clone)]
6+
pub struct Intersperse<I: Iterator>
7+
where
8+
I::Item: Clone,
9+
{
10+
separator: I::Item,
11+
iter: Peekable<I>,
12+
needs_sep: bool,
13+
}
14+
15+
impl<I: Iterator> Intersperse<I>
16+
where
17+
I::Item: Clone,
18+
{
19+
pub(in crate::iter) fn new(iter: I, separator: I::Item) -> Self {
20+
Self { iter: iter.peekable(), separator, needs_sep: false }
21+
}
22+
}
23+
24+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
25+
impl<I> Iterator for Intersperse<I>
26+
where
27+
I: Iterator,
28+
I::Item: Clone,
29+
{
30+
type Item = I::Item;
31+
32+
#[inline]
33+
fn next(&mut self) -> Option<I::Item> {
34+
if self.needs_sep && self.iter.peek().is_some() {
35+
self.needs_sep = false;
36+
Some(self.separator.clone())
37+
} else {
38+
self.needs_sep = true;
39+
self.iter.next()
40+
}
41+
}
42+
43+
fn fold<B, F>(mut self, init: B, mut f: F) -> B
44+
where
45+
Self: Sized,
46+
F: FnMut(B, Self::Item) -> B,
47+
{
48+
let mut accum = init;
49+
50+
// Use `peek()` first to avoid calling `next()` on an empty iterator.
51+
if !self.needs_sep || self.iter.peek().is_some() {
52+
if let Some(x) = self.iter.next() {
53+
accum = f(accum, x);
54+
}
55+
}
56+
57+
let element = &self.separator;
58+
59+
self.iter.fold(accum, |mut accum, x| {
60+
accum = f(accum, element.clone());
61+
accum = f(accum, x);
62+
accum
63+
})
64+
}
65+
66+
fn size_hint(&self) -> (usize, Option<usize>) {
67+
let (lo, hi) = self.iter.size_hint();
68+
let next_is_elem = !self.needs_sep;
69+
let lo = lo.saturating_sub(next_is_elem as usize).saturating_add(lo);
70+
let hi = match hi {
71+
Some(hi) => hi.saturating_sub(next_is_elem as usize).checked_add(hi),
72+
None => None,
73+
};
74+
(lo, hi)
75+
}
76+
}

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

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod filter_map;
1111
mod flatten;
1212
mod fuse;
1313
mod inspect;
14+
mod intersperse;
1415
mod map;
1516
mod map_while;
1617
mod peekable;
@@ -41,6 +42,9 @@ pub use self::flatten::Flatten;
4142
#[stable(feature = "iter_copied", since = "1.36.0")]
4243
pub use self::copied::Copied;
4344

45+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
46+
pub use self::intersperse::Intersperse;
47+
4448
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
4549
pub use self::map_while::MapWhile;
4650

library/core/src/iter/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@ pub use self::adapters::Cloned;
395395
pub use self::adapters::Copied;
396396
#[stable(feature = "iterator_flatten", since = "1.29.0")]
397397
pub use self::adapters::Flatten;
398+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
399+
pub use self::adapters::Intersperse;
398400
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
399401
pub use self::adapters::MapWhile;
400402
#[unstable(feature = "inplace_iteration", issue = "none")]

library/core/src/iter/traits/iterator.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::ops::{Add, ControlFlow, Try};
88
use super::super::TrustedRandomAccess;
99
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse};
1010
use super::super::{FlatMap, Flatten};
11-
use super::super::{FromIterator, Product, Sum, Zip};
11+
use super::super::{FromIterator, Intersperse, Product, Sum, Zip};
1212
use super::super::{
1313
Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile,
1414
};
@@ -569,6 +569,28 @@ pub trait Iterator {
569569
Zip::new(self, other.into_iter())
570570
}
571571

572+
/// Places a copy of `separator` between all elements.
573+
///
574+
/// # Examples
575+
///
576+
/// Basic usage:
577+
///
578+
/// ```
579+
/// #![feature(iter_intersperse)]
580+
///
581+
/// let hello = ["Hello", "World"].iter().copied().intersperse(" ").collect::<String>();
582+
/// assert_eq!(hello, "Hello World");
583+
/// ```
584+
#[inline]
585+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
586+
fn intersperse(self, separator: Self::Item) -> Intersperse<Self>
587+
where
588+
Self: Sized,
589+
Self::Item: Clone,
590+
{
591+
Intersperse::new(self, separator)
592+
}
593+
572594
/// Takes a closure and creates an iterator which calls that closure on each
573595
/// element.
574596
///

library/core/tests/iter.rs

+82
Original file line numberDiff line numberDiff line change
@@ -3505,3 +3505,85 @@ pub fn extend_for_unit() {
35053505
}
35063506
assert_eq!(x, 5);
35073507
}
3508+
3509+
#[test]
3510+
fn test_intersperse() {
3511+
let xs = ["a", "", "b", "c"];
3512+
let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect();
3513+
let text: String = v.concat();
3514+
assert_eq!(text, "a, , b, c".to_string());
3515+
3516+
let ys = [0, 1, 2, 3];
3517+
let mut it = ys[..0].iter().map(|x| *x).intersperse(1);
3518+
assert!(it.next() == None);
3519+
}
3520+
3521+
#[test]
3522+
fn test_intersperse_size_hint() {
3523+
let xs = ["a", "", "b", "c"];
3524+
let mut iter = xs.iter().map(|x| x.clone()).intersperse(", ");
3525+
assert_eq!(iter.size_hint(), (7, Some(7)));
3526+
3527+
assert_eq!(iter.next(), Some("a"));
3528+
assert_eq!(iter.size_hint(), (6, Some(6)));
3529+
assert_eq!(iter.next(), Some(", "));
3530+
assert_eq!(iter.size_hint(), (5, Some(5)));
3531+
3532+
assert_eq!([].iter().intersperse(&()).size_hint(), (0, Some(0)));
3533+
}
3534+
3535+
#[test]
3536+
fn test_fold_specialization_intersperse() {
3537+
let mut iter = (1..2).intersperse(0);
3538+
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
3539+
3540+
let mut iter = (1..3).intersperse(0);
3541+
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
3542+
3543+
let mut iter = (1..4).intersperse(0);
3544+
iter.clone().for_each(|x| assert_eq!(Some(x), iter.next()));
3545+
}
3546+
3547+
#[test]
3548+
fn test_try_fold_specialization_intersperse_ok() {
3549+
let mut iter = (1..2).intersperse(0);
3550+
iter.clone().try_for_each(|x| {
3551+
assert_eq!(Some(x), iter.next());
3552+
Some(())
3553+
});
3554+
3555+
let mut iter = (1..3).intersperse(0);
3556+
iter.clone().try_for_each(|x| {
3557+
assert_eq!(Some(x), iter.next());
3558+
Some(())
3559+
});
3560+
3561+
let mut iter = (1..4).intersperse(0);
3562+
iter.clone().try_for_each(|x| {
3563+
assert_eq!(Some(x), iter.next());
3564+
Some(())
3565+
});
3566+
}
3567+
3568+
#[test]
3569+
fn test_try_fold_specialization_intersperse_err() {
3570+
let orig_iter = ["a", "b"].iter().copied().intersperse("-");
3571+
3572+
// Abort after the first item.
3573+
let mut iter = orig_iter.clone();
3574+
iter.try_for_each(|_| None::<()>);
3575+
assert_eq!(iter.next(), Some("-"));
3576+
assert_eq!(iter.next(), Some("b"));
3577+
assert_eq!(iter.next(), None);
3578+
3579+
// Abort after the second item.
3580+
let mut iter = orig_iter.clone();
3581+
iter.try_for_each(|item| if item == "-" { None } else { Some(()) });
3582+
assert_eq!(iter.next(), Some("b"));
3583+
assert_eq!(iter.next(), None);
3584+
3585+
// Abort after the third item.
3586+
let mut iter = orig_iter.clone();
3587+
iter.try_for_each(|item| if item == "b" { None } else { Some(()) });
3588+
assert_eq!(iter.next(), None);
3589+
}

library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#![feature(array_value_iter)]
5252
#![feature(iter_advance_by)]
5353
#![feature(iter_partition_in_place)]
54+
#![feature(iter_intersperse)]
5455
#![feature(iter_is_partitioned)]
5556
#![feature(iter_order_by)]
5657
#![feature(cmp_min_max_by)]

src/librustdoc/clean/utils.rs

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::clean::{
88
};
99
use crate::core::DocContext;
1010

11-
use itertools::Itertools;
1211
use rustc_data_structures::fx::FxHashSet;
1312
use rustc_hir as hir;
1413
use rustc_hir::def::{DefKind, Res};

src/librustdoc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#![feature(type_ascription)]
1818
#![feature(split_inclusive)]
1919
#![feature(str_split_once)]
20+
#![feature(iter_intersperse)]
2021
#![recursion_limit = "256"]
2122

2223
#[macro_use]

0 commit comments

Comments
 (0)