Skip to main content

core/iter/adapters/
map.rs

1use crate::fmt;
2#[cfg(not(feature = "ferrocene_subset"))]
3use crate::iter::adapters::zip::try_get_unchecked;
4#[cfg(not(feature = "ferrocene_subset"))]
5use crate::iter::adapters::{SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
6#[cfg(not(feature = "ferrocene_subset"))]
7use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedLen, UncheckedIterator};
8#[cfg(not(feature = "ferrocene_subset"))]
9use crate::num::NonZero;
10use crate::ops::Try;
11
12// Ferrocene addition: imports for certified subset
13#[cfg(feature = "ferrocene_subset")]
14#[rustfmt::skip]
15use crate::iter::{TrustedLen, UncheckedIterator};
16
17/// An iterator that maps the values of `iter` with `f`.
18///
19/// This `struct` is created by the [`map`] method on [`Iterator`]. See its
20/// documentation for more.
21///
22/// [`map`]: Iterator::map
23/// [`Iterator`]: trait.Iterator.html
24///
25/// # Notes about side effects
26///
27/// The [`map`] iterator implements [`DoubleEndedIterator`], meaning that
28/// you can also [`map`] backwards:
29///
30/// ```rust
31/// let v: Vec<i32> = [1, 2, 3].into_iter().map(|x| x + 1).rev().collect();
32///
33/// assert_eq!(v, [4, 3, 2]);
34/// ```
35///
36/// [`DoubleEndedIterator`]: trait.DoubleEndedIterator.html
37///
38/// But if your closure has state, iterating backwards may act in a way you do
39/// not expect. Let's go through an example. First, in the forward direction:
40///
41/// ```rust
42/// let mut c = 0;
43///
44/// for pair in ['a', 'b', 'c'].into_iter()
45///                                .map(|letter| { c += 1; (letter, c) }) {
46///     println!("{pair:?}");
47/// }
48/// ```
49///
50/// This will print `('a', 1), ('b', 2), ('c', 3)`.
51///
52/// Now consider this twist where we add a call to `rev`. This version will
53/// print `('c', 1), ('b', 2), ('a', 3)`. Note that the letters are reversed,
54/// but the values of the counter still go in order. This is because `map()` is
55/// still being called lazily on each item, but we are popping items off the
56/// back of the vector now, instead of shifting them from the front.
57///
58/// ```rust
59/// let mut c = 0;
60///
61/// for pair in ['a', 'b', 'c'].into_iter()
62///                                .map(|letter| { c += 1; (letter, c) })
63///                                .rev() {
64///     println!("{pair:?}");
65/// }
66/// ```
67#[must_use = "iterators are lazy and do nothing unless consumed"]
68#[stable(feature = "rust1", since = "1.0.0")]
69#[derive(Clone)]
70pub struct Map<I, F> {
71    // Used for `SplitWhitespace` and `SplitAsciiWhitespace` `as_str` methods
72    pub(crate) iter: I,
73    f: F,
74}
75
76impl<I, F> Map<I, F> {
77    pub(in crate::iter) fn new(iter: I, f: F) -> Map<I, F> {
78        Map { iter, f }
79    }
80
81    pub(crate) fn into_inner(self) -> I {
82        self.iter
83    }
84}
85
86#[stable(feature = "core_impl_debug", since = "1.9.0")]
87impl<I: fmt::Debug, F> fmt::Debug for Map<I, F> {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        f.debug_struct("Map").field("iter", &self.iter).finish()
90    }
91}
92
93fn map_fold<T, B, Acc>(
94    mut f: impl FnMut(T) -> B,
95    mut g: impl FnMut(Acc, B) -> Acc,
96) -> impl FnMut(Acc, T) -> Acc {
97    move |acc, elt| g(acc, f(elt))
98}
99
100fn map_try_fold<'a, T, B, Acc, R>(
101    f: &'a mut impl FnMut(T) -> B,
102    mut g: impl FnMut(Acc, B) -> R + 'a,
103) -> impl FnMut(Acc, T) -> R + 'a {
104    move |acc, elt| g(acc, f(elt))
105}
106
107#[stable(feature = "rust1", since = "1.0.0")]
108impl<B, I: Iterator, F> Iterator for Map<I, F>
109where
110    F: FnMut(I::Item) -> B,
111{
112    type Item = B;
113
114    #[inline]
115    fn next(&mut self) -> Option<B> {
116        self.iter.next().map(&mut self.f)
117    }
118
119    #[inline]
120    fn size_hint(&self) -> (usize, Option<usize>) {
121        self.iter.size_hint()
122    }
123
124    fn try_fold<Acc, G, R>(&mut self, init: Acc, g: G) -> R
125    where
126        Self: Sized,
127        G: FnMut(Acc, Self::Item) -> R,
128        R: Try<Output = Acc>,
129    {
130        self.iter.try_fold(init, map_try_fold(&mut self.f, g))
131    }
132
133    fn fold<Acc, G>(self, init: Acc, g: G) -> Acc
134    where
135        G: FnMut(Acc, Self::Item) -> Acc,
136    {
137        self.iter.fold(init, map_fold(self.f, g))
138    }
139
140    #[inline]
141    #[cfg(not(feature = "ferrocene_subset"))]
142    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> B
143    where
144        Self: TrustedRandomAccessNoCoerce,
145    {
146        // SAFETY: the caller must uphold the contract for
147        // `Iterator::__iterator_get_unchecked`.
148        unsafe { (self.f)(try_get_unchecked(&mut self.iter, idx)) }
149    }
150}
151
152#[stable(feature = "rust1", since = "1.0.0")]
153impl<B, I: DoubleEndedIterator, F> DoubleEndedIterator for Map<I, F>
154where
155    F: FnMut(I::Item) -> B,
156{
157    #[inline]
158    fn next_back(&mut self) -> Option<B> {
159        self.iter.next_back().map(&mut self.f)
160    }
161
162    fn try_rfold<Acc, G, R>(&mut self, init: Acc, g: G) -> R
163    where
164        Self: Sized,
165        G: FnMut(Acc, Self::Item) -> R,
166        R: Try<Output = Acc>,
167    {
168        self.iter.try_rfold(init, map_try_fold(&mut self.f, g))
169    }
170
171    fn rfold<Acc, G>(self, init: Acc, g: G) -> Acc
172    where
173        G: FnMut(Acc, Self::Item) -> Acc,
174    {
175        self.iter.rfold(init, map_fold(self.f, g))
176    }
177}
178
179#[stable(feature = "rust1", since = "1.0.0")]
180#[cfg(not(feature = "ferrocene_subset"))]
181impl<B, I: ExactSizeIterator, F> ExactSizeIterator for Map<I, F>
182where
183    F: FnMut(I::Item) -> B,
184{
185    fn len(&self) -> usize {
186        self.iter.len()
187    }
188
189    fn is_empty(&self) -> bool {
190        self.iter.is_empty()
191    }
192}
193
194#[stable(feature = "fused", since = "1.26.0")]
195#[cfg(not(feature = "ferrocene_subset"))]
196impl<B, I: FusedIterator, F> FusedIterator for Map<I, F> where F: FnMut(I::Item) -> B {}
197
198#[unstable(issue = "none", feature = "trusted_fused")]
199#[cfg(not(feature = "ferrocene_subset"))]
200unsafe impl<I: TrustedFused, F> TrustedFused for Map<I, F> {}
201
202#[unstable(feature = "trusted_len", issue = "37572")]
203unsafe impl<B, I, F> TrustedLen for Map<I, F>
204where
205    I: TrustedLen,
206    F: FnMut(I::Item) -> B,
207{
208}
209
210impl<B, I, F> UncheckedIterator for Map<I, F>
211where
212    I: UncheckedIterator,
213    F: FnMut(I::Item) -> B,
214{
215    unsafe fn next_unchecked(&mut self) -> B {
216        // SAFETY: `Map` is 1:1 with the inner iterator, so if the caller promised
217        // that there's an element left, the inner iterator has one too.
218        let item = unsafe { self.iter.next_unchecked() };
219        (self.f)(item)
220    }
221}
222
223#[doc(hidden)]
224#[unstable(feature = "trusted_random_access", issue = "none")]
225#[cfg(not(feature = "ferrocene_subset"))]
226unsafe impl<I, F> TrustedRandomAccess for Map<I, F> where I: TrustedRandomAccess {}
227
228#[doc(hidden)]
229#[unstable(feature = "trusted_random_access", issue = "none")]
230#[cfg(not(feature = "ferrocene_subset"))]
231unsafe impl<I, F> TrustedRandomAccessNoCoerce for Map<I, F>
232where
233    I: TrustedRandomAccessNoCoerce,
234{
235    const MAY_HAVE_SIDE_EFFECT: bool = true;
236}
237
238#[unstable(issue = "none", feature = "inplace_iteration")]
239#[cfg(not(feature = "ferrocene_subset"))]
240unsafe impl<I, F> SourceIter for Map<I, F>
241where
242    I: SourceIter,
243{
244    type Source = I::Source;
245
246    #[inline]
247    unsafe fn as_inner(&mut self) -> &mut I::Source {
248        // SAFETY: unsafe function forwarding to unsafe function with the same requirements
249        unsafe { SourceIter::as_inner(&mut self.iter) }
250    }
251}
252
253#[unstable(issue = "none", feature = "inplace_iteration")]
254#[cfg(not(feature = "ferrocene_subset"))]
255unsafe impl<I: InPlaceIterable, F> InPlaceIterable for Map<I, F> {
256    const EXPAND_BY: Option<NonZero<usize>> = I::EXPAND_BY;
257    const MERGE_BY: Option<NonZero<usize>> = I::MERGE_BY;
258}