Skip to content

Commit 7c4d34f

Browse files
authored
Rollup merge of rust-lang#80567 - lukaslueg:intersperse_with, r=m-ou-se
Add Iterator::intersperse_with This is a follow-up to rust-lang#79479, tracking in rust-lang#79524, as discussed rust-lang#79479 (comment). ~~Note that I had to manually implement `Clone` and `Debug` because `derive` insists on placing a `Clone`-bound on the struct-definition, which is too narrow. There is a long-standing issue # for this somewhere around here :-)~~ Also, note that I refactored the guts of `Intersperse` into private functions and re-used them in `IntersperseWith`, so I also went light on duplicating all the tests. If this is suitable to be merged, the tracking issue should be updated, since it only mentions `intersperse`. Happy New Year! r? `@m-ou-se`
2 parents 4ac831b + 9b2f085 commit 7c4d34f

File tree

5 files changed

+202
-27
lines changed

5 files changed

+202
-27
lines changed

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

+133-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use super::Peekable;
22

33
/// An iterator adapter that places a separator between all elements.
4+
///
5+
/// This `struct` is created by [`Iterator::intersperse`]. See its documentation
6+
/// for more information.
47
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
58
#[derive(Debug, Clone)]
69
pub struct Intersperse<I: Iterator>
@@ -40,37 +43,146 @@ where
4043
}
4144
}
4245

43-
fn fold<B, F>(mut self, init: B, mut f: F) -> B
46+
fn fold<B, F>(self, init: B, f: F) -> B
4447
where
4548
Self: Sized,
4649
F: FnMut(B, Self::Item) -> B,
4750
{
48-
let mut accum = init;
51+
let separator = self.separator;
52+
intersperse_fold(self.iter, init, f, move || separator.clone(), self.needs_sep)
53+
}
54+
55+
fn size_hint(&self) -> (usize, Option<usize>) {
56+
intersperse_size_hint(&self.iter, self.needs_sep)
57+
}
58+
}
59+
60+
/// An iterator adapter that places a separator between all elements.
61+
///
62+
/// This `struct` is created by [`Iterator::intersperse_with`]. See its
63+
/// documentation for more information.
64+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
65+
pub struct IntersperseWith<I, G>
66+
where
67+
I: Iterator,
68+
{
69+
separator: G,
70+
iter: Peekable<I>,
71+
needs_sep: bool,
72+
}
73+
74+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
75+
impl<I, G> crate::fmt::Debug for IntersperseWith<I, G>
76+
where
77+
I: Iterator + crate::fmt::Debug,
78+
I::Item: crate::fmt::Debug,
79+
G: crate::fmt::Debug,
80+
{
81+
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
82+
f.debug_struct("IntersperseWith")
83+
.field("separator", &self.separator)
84+
.field("iter", &self.iter)
85+
.field("needs_sep", &self.needs_sep)
86+
.finish()
87+
}
88+
}
4989

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-
}
90+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
91+
impl<I, G> crate::clone::Clone for IntersperseWith<I, G>
92+
where
93+
I: Iterator + crate::clone::Clone,
94+
I::Item: crate::clone::Clone,
95+
G: Clone,
96+
{
97+
fn clone(&self) -> Self {
98+
IntersperseWith {
99+
separator: self.separator.clone(),
100+
iter: self.iter.clone(),
101+
needs_sep: self.needs_sep.clone(),
55102
}
103+
}
104+
}
56105

57-
let element = &self.separator;
106+
impl<I, G> IntersperseWith<I, G>
107+
where
108+
I: Iterator,
109+
G: FnMut() -> I::Item,
110+
{
111+
pub(in crate::iter) fn new(iter: I, separator: G) -> Self {
112+
Self { iter: iter.peekable(), separator, needs_sep: false }
113+
}
114+
}
58115

59-
self.iter.fold(accum, |mut accum, x| {
60-
accum = f(accum, element.clone());
61-
accum = f(accum, x);
62-
accum
63-
})
116+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
117+
impl<I, G> Iterator for IntersperseWith<I, G>
118+
where
119+
I: Iterator,
120+
G: FnMut() -> I::Item,
121+
{
122+
type Item = I::Item;
123+
124+
#[inline]
125+
fn next(&mut self) -> Option<I::Item> {
126+
if self.needs_sep && self.iter.peek().is_some() {
127+
self.needs_sep = false;
128+
Some((self.separator)())
129+
} else {
130+
self.needs_sep = true;
131+
self.iter.next()
132+
}
133+
}
134+
135+
fn fold<B, F>(self, init: B, f: F) -> B
136+
where
137+
Self: Sized,
138+
F: FnMut(B, Self::Item) -> B,
139+
{
140+
intersperse_fold(self.iter, init, f, self.separator, self.needs_sep)
64141
}
65142

66143
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)
144+
intersperse_size_hint(&self.iter, self.needs_sep)
75145
}
76146
}
147+
148+
fn intersperse_size_hint<I>(iter: &I, needs_sep: bool) -> (usize, Option<usize>)
149+
where
150+
I: Iterator,
151+
{
152+
let (lo, hi) = iter.size_hint();
153+
let next_is_elem = !needs_sep;
154+
let lo = lo.saturating_sub(next_is_elem as usize).saturating_add(lo);
155+
let hi = match hi {
156+
Some(hi) => hi.saturating_sub(next_is_elem as usize).checked_add(hi),
157+
None => None,
158+
};
159+
(lo, hi)
160+
}
161+
162+
fn intersperse_fold<I, B, F, G>(
163+
mut iter: Peekable<I>,
164+
init: B,
165+
mut f: F,
166+
mut separator: G,
167+
needs_sep: bool,
168+
) -> B
169+
where
170+
I: Iterator,
171+
F: FnMut(B, I::Item) -> B,
172+
G: FnMut() -> I::Item,
173+
{
174+
let mut accum = init;
175+
176+
// Use `peek()` first to avoid calling `next()` on an empty iterator.
177+
if !needs_sep || iter.peek().is_some() {
178+
if let Some(x) = iter.next() {
179+
accum = f(accum, x);
180+
}
181+
}
182+
183+
iter.fold(accum, |mut accum, x| {
184+
accum = f(accum, separator());
185+
accum = f(accum, x);
186+
accum
187+
})
188+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub use self::flatten::Flatten;
4343
pub use self::copied::Copied;
4444

4545
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
46-
pub use self::intersperse::Intersperse;
46+
pub use self::intersperse::{Intersperse, IntersperseWith};
4747

4848
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
4949
pub use self::map_while::MapWhile;

library/core/src/iter/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,6 @@ 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;
400398
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
401399
pub use self::adapters::MapWhile;
402400
#[unstable(feature = "inplace_iteration", issue = "none")]
@@ -410,6 +408,8 @@ pub use self::adapters::{
410408
Chain, Cycle, Enumerate, Filter, FilterMap, FlatMap, Fuse, Inspect, Map, Peekable, Rev, Scan,
411409
Skip, SkipWhile, Take, TakeWhile, Zip,
412410
};
411+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
412+
pub use self::adapters::{Intersperse, IntersperseWith};
413413

414414
pub(crate) use self::adapters::process_results;
415415

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

+36-3
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, Intersperse, Product, Sum, Zip};
11+
use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip};
1212
use super::super::{
1313
Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile,
1414
};
@@ -571,16 +571,22 @@ pub trait Iterator {
571571

572572
/// Places a copy of `separator` between all elements.
573573
///
574+
/// In case the separator does not implement [`Clone`] or needs to be
575+
/// computed every time, use [`intersperse_with`].
576+
///
574577
/// # Examples
575578
///
576579
/// Basic usage:
577580
///
578581
/// ```
579582
/// #![feature(iter_intersperse)]
580583
///
581-
/// let hello = ["Hello", "World"].iter().copied().intersperse(" ").collect::<String>();
582-
/// assert_eq!(hello, "Hello World");
584+
/// let hello = ["Hello", "World", "!"].iter().copied().intersperse(" ").collect::<String>();
585+
/// assert_eq!(hello, "Hello World !");
583586
/// ```
587+
///
588+
/// [`Clone`]: crate::clone::Clone
589+
/// [`intersperse_with`]: Iterator::intersperse_with
584590
#[inline]
585591
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
586592
fn intersperse(self, separator: Self::Item) -> Intersperse<Self>
@@ -591,6 +597,33 @@ pub trait Iterator {
591597
Intersperse::new(self, separator)
592598
}
593599

600+
/// Places an element generated by `separator` between all elements.
601+
///
602+
/// # Examples
603+
///
604+
/// Basic usage:
605+
///
606+
/// ```
607+
/// #![feature(iter_intersperse)]
608+
///
609+
/// let src = ["Hello", "to", "all", "people", "!!"].iter().copied();
610+
///
611+
/// let mut happy_emojis = [" ❤️ ", " 😀 "].iter().copied();
612+
/// let separator = || happy_emojis.next().unwrap_or(" 🦀 ");
613+
///
614+
/// let result = src.intersperse_with(separator).collect::<String>();
615+
/// assert_eq!(result, "Hello ❤️ to 😀 all 🦀 people 🦀 !!");
616+
/// ```
617+
#[inline]
618+
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
619+
fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>
620+
where
621+
Self: Sized,
622+
G: FnMut() -> Self::Item,
623+
{
624+
IntersperseWith::new(self, separator)
625+
}
626+
594627
/// Takes a closure and creates an iterator which calls that closure on each
595628
/// element.
596629
///

library/core/tests/iter.rs

+30
Original file line numberDiff line numberDiff line change
@@ -3508,6 +3508,12 @@ pub fn extend_for_unit() {
35083508

35093509
#[test]
35103510
fn test_intersperse() {
3511+
let v = std::iter::empty().intersperse(0u32).collect::<Vec<_>>();
3512+
assert_eq!(v, vec![]);
3513+
3514+
let v = std::iter::once(1).intersperse(0).collect::<Vec<_>>();
3515+
assert_eq!(v, vec![1]);
3516+
35113517
let xs = ["a", "", "b", "c"];
35123518
let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect();
35133519
let text: String = v.concat();
@@ -3520,6 +3526,9 @@ fn test_intersperse() {
35203526

35213527
#[test]
35223528
fn test_intersperse_size_hint() {
3529+
let iter = std::iter::empty::<i32>().intersperse(0);
3530+
assert_eq!(iter.size_hint(), (0, Some(0)));
3531+
35233532
let xs = ["a", "", "b", "c"];
35243533
let mut iter = xs.iter().map(|x| x.clone()).intersperse(", ");
35253534
assert_eq!(iter.size_hint(), (7, Some(7)));
@@ -3587,3 +3596,24 @@ fn test_try_fold_specialization_intersperse_err() {
35873596
iter.try_for_each(|item| if item == "b" { None } else { Some(()) });
35883597
assert_eq!(iter.next(), None);
35893598
}
3599+
3600+
#[test]
3601+
fn test_intersperse_with() {
3602+
#[derive(PartialEq, Debug)]
3603+
struct NotClone {
3604+
u: u32,
3605+
}
3606+
let r = vec![NotClone { u: 0 }, NotClone { u: 1 }]
3607+
.into_iter()
3608+
.intersperse_with(|| NotClone { u: 2 })
3609+
.collect::<Vec<_>>();
3610+
assert_eq!(r, vec![NotClone { u: 0 }, NotClone { u: 2 }, NotClone { u: 1 }]);
3611+
3612+
let mut ctr = 100;
3613+
let separator = || {
3614+
ctr *= 2;
3615+
ctr
3616+
};
3617+
let r = (0..3).intersperse_with(separator).collect::<Vec<_>>();
3618+
assert_eq!(r, vec![0, 200, 1, 400, 2]);
3619+
}

0 commit comments

Comments
 (0)