Skip to main content

core/iter/adapters/
zip.rs

1use crate::cmp;
2use crate::fmt::{self, Debug};
3use crate::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen};
4use crate::num::NonZero;
5
6/// An iterator that iterates two other iterators simultaneously.
7///
8/// This `struct` is created by [`zip`] or [`Iterator::zip`].
9/// See their documentation for more.
10#[derive(Clone)]
11#[must_use = "iterators are lazy and do nothing unless consumed"]
12#[stable(feature = "rust1", since = "1.0.0")]
13pub struct Zip<A, B> {
14    a: A,
15    b: B,
16    // index, len and a_len are only used by the specialized version of zip
17    index: usize,
18    len: usize,
19}
20impl<A: Iterator, B: Iterator> Zip<A, B> {
21    pub(in crate::iter) fn new(a: A, b: B) -> Zip<A, B> {
22        ZipImpl::new(a, b)
23    }
24    fn super_nth(&mut self, mut n: usize) -> Option<(A::Item, B::Item)> {
25        while let Some(x) = Iterator::next(self) {
26            if n == 0 {
27                return Some(x);
28            }
29            n -= 1;
30        }
31        None
32    }
33}
34
35/// Converts the arguments to iterators and zips them.
36///
37/// See the documentation of [`Iterator::zip`] for more.
38///
39/// # Examples
40///
41/// ```
42/// use std::iter::zip;
43///
44/// let xs = [1, 2, 3];
45/// let ys = [4, 5, 6];
46///
47/// let mut iter = zip(xs, ys);
48///
49/// assert_eq!(iter.next().unwrap(), (1, 4));
50/// assert_eq!(iter.next().unwrap(), (2, 5));
51/// assert_eq!(iter.next().unwrap(), (3, 6));
52/// assert!(iter.next().is_none());
53///
54/// // Nested zips are also possible:
55/// let zs = [7, 8, 9];
56///
57/// let mut iter = zip(zip(xs, ys), zs);
58///
59/// assert_eq!(iter.next().unwrap(), ((1, 4), 7));
60/// assert_eq!(iter.next().unwrap(), ((2, 5), 8));
61/// assert_eq!(iter.next().unwrap(), ((3, 6), 9));
62/// assert!(iter.next().is_none());
63/// ```
64#[stable(feature = "iter_zip", since = "1.59.0")]
65pub fn zip<A, B>(a: A, b: B) -> Zip<A::IntoIter, B::IntoIter>
66where
67    A: IntoIterator,
68    B: IntoIterator,
69{
70    ZipImpl::new(a.into_iter(), b.into_iter())
71}
72
73#[stable(feature = "rust1", since = "1.0.0")]
74impl<A, B> Iterator for Zip<A, B>
75where
76    A: Iterator,
77    B: Iterator,
78{
79    type Item = (A::Item, B::Item);
80
81    #[inline]
82    fn next(&mut self) -> Option<Self::Item> {
83        ZipImpl::next(self)
84    }
85
86    #[inline]
87    fn size_hint(&self) -> (usize, Option<usize>) {
88        ZipImpl::size_hint(self)
89    }
90
91    #[inline]
92    fn nth(&mut self, n: usize) -> Option<Self::Item> {
93        ZipImpl::nth(self, n)
94    }
95
96    #[inline]
97    fn fold<Acc, F>(self, init: Acc, f: F) -> Acc
98    where
99        F: FnMut(Acc, Self::Item) -> Acc,
100    {
101        ZipImpl::fold(self, init, f)
102    }
103
104    #[inline]
105    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
106    where
107        Self: TrustedRandomAccessNoCoerce,
108    {
109        // SAFETY: `ZipImpl::__iterator_get_unchecked` has same safety
110        // requirements as `Iterator::__iterator_get_unchecked`.
111        unsafe { ZipImpl::get_unchecked(self, idx) }
112    }
113}
114
115#[stable(feature = "rust1", since = "1.0.0")]
116impl<A, B> DoubleEndedIterator for Zip<A, B>
117where
118    A: DoubleEndedIterator + ExactSizeIterator,
119    B: DoubleEndedIterator + ExactSizeIterator,
120{
121    #[inline]
122    fn next_back(&mut self) -> Option<(A::Item, B::Item)> {
123        ZipImpl::next_back(self)
124    }
125}
126
127// Zip specialization trait
128trait ZipImpl<A, B> {
129    type Item;
130    fn new(a: A, b: B) -> Self;
131    fn next(&mut self) -> Option<Self::Item>;
132    fn size_hint(&self) -> (usize, Option<usize>);
133    fn nth(&mut self, n: usize) -> Option<Self::Item>;
134    fn next_back(&mut self) -> Option<Self::Item>
135    where
136        A: DoubleEndedIterator + ExactSizeIterator,
137        B: DoubleEndedIterator + ExactSizeIterator;
138    fn fold<Acc, F>(self, init: Acc, f: F) -> Acc
139    where
140        F: FnMut(Acc, Self::Item) -> Acc;
141    // This has the same safety requirements as `Iterator::__iterator_get_unchecked`
142    unsafe fn get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item
143    where
144        Self: Iterator + TrustedRandomAccessNoCoerce;
145}
146
147// Work around limitations of specialization, requiring `default` impls to be repeated
148// in intermediary impls.
149macro_rules! zip_impl_general_defaults {
150    () => {
151        default fn new(a: A, b: B) -> Self {
152            Zip {
153                a,
154                b,
155                index: 0, // unused
156                len: 0,   // unused
157            }
158        }
159
160        #[inline]
161        default fn next(&mut self) -> Option<(A::Item, B::Item)> {
162            let x = self.a.next()?;
163            let y = self.b.next()?;
164            Some((x, y))
165        }
166
167        #[inline]
168        default fn nth(&mut self, n: usize) -> Option<Self::Item> {
169            self.super_nth(n)
170        }
171
172        #[inline]
173        default fn next_back(&mut self) -> Option<(A::Item, B::Item)>
174        where
175            A: DoubleEndedIterator + ExactSizeIterator,
176            B: DoubleEndedIterator + ExactSizeIterator,
177        {
178            // The function body below only uses `self.a/b.len()` and `self.a/b.next_back()`
179            // and doesn’t call `next_back` too often, so this implementation is safe in
180            // the `TrustedRandomAccessNoCoerce` specialization
181
182            let a_sz = self.a.len();
183            let b_sz = self.b.len();
184            if a_sz != b_sz {
185                // Adjust a, b to equal length
186                if a_sz > b_sz {
187                    for _ in 0..a_sz - b_sz {
188                        self.a.next_back();
189                    }
190                } else {
191                    for _ in 0..b_sz - a_sz {
192                        self.b.next_back();
193                    }
194                }
195            }
196            match (self.a.next_back(), self.b.next_back()) {
197                (Some(x), Some(y)) => Some((x, y)),
198                (None, None) => None,
199                _ => unreachable!(),
200            }
201        }
202    };
203}
204
205// General Zip impl
206impl<A, B> ZipImpl<A, B> for Zip<A, B>
207where
208    A: Iterator,
209    B: Iterator,
210{
211    type Item = (A::Item, B::Item);
212
213    zip_impl_general_defaults! {}
214
215    #[inline]
216    default fn size_hint(&self) -> (usize, Option<usize>) {
217        let (a_lower, a_upper) = self.a.size_hint();
218        let (b_lower, b_upper) = self.b.size_hint();
219
220        let lower = cmp::min(a_lower, b_lower);
221
222        let upper = match (a_upper, b_upper) {
223            (Some(x), Some(y)) => Some(cmp::min(x, y)),
224            (Some(x), None) => Some(x),
225            (None, Some(y)) => Some(y),
226            (None, None) => None,
227        };
228
229        (lower, upper)
230    }
231
232    default unsafe fn get_unchecked(&mut self, _idx: usize) -> <Self as Iterator>::Item
233    where
234        Self: TrustedRandomAccessNoCoerce,
235    {
236        unreachable!("Always specialized");
237    }
238
239    #[inline]
240    default fn fold<Acc, F>(self, init: Acc, f: F) -> Acc
241    where
242        F: FnMut(Acc, Self::Item) -> Acc,
243    {
244        SpecFold::spec_fold(self, init, f)
245    }
246}
247
248impl<A, B> ZipImpl<A, B> for Zip<A, B>
249where
250    A: TrustedRandomAccessNoCoerce + Iterator,
251    B: TrustedRandomAccessNoCoerce + Iterator,
252{
253    zip_impl_general_defaults! {}
254
255    #[inline]
256    default fn size_hint(&self) -> (usize, Option<usize>) {
257        let size = cmp::min(self.a.size(), self.b.size());
258        (size, Some(size))
259    }
260
261    #[inline]
262    unsafe fn get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item {
263        let idx = self.index + idx;
264        // SAFETY: the caller must uphold the contract for
265        // `Iterator::__iterator_get_unchecked`.
266        unsafe { (self.a.__iterator_get_unchecked(idx), self.b.__iterator_get_unchecked(idx)) }
267    }
268
269    #[inline]
270    fn fold<Acc, F>(mut self, init: Acc, mut f: F) -> Acc
271    where
272        F: FnMut(Acc, Self::Item) -> Acc,
273    {
274        let mut accum = init;
275        let len = ZipImpl::size_hint(&self).0;
276        for i in 0..len {
277            // SAFETY: since Self: TrustedRandomAccessNoCoerce we can trust the size-hint to
278            // calculate the length and then use that to do unchecked iteration.
279            // fold consumes the iterator so we don't need to fixup any state.
280            unsafe {
281                accum = f(accum, self.get_unchecked(i));
282            }
283        }
284        accum
285    }
286}
287
288impl<A, B> ZipImpl<A, B> for Zip<A, B>
289where
290    A: TrustedRandomAccess + Iterator,
291    B: TrustedRandomAccess + Iterator,
292{
293    fn new(a: A, b: B) -> Self {
294        let len = cmp::min(a.size(), b.size());
295        Zip { a, b, index: 0, len }
296    }
297
298    #[inline]
299    fn next(&mut self) -> Option<(A::Item, B::Item)> {
300        if self.index < self.len {
301            let i = self.index;
302            // since get_unchecked executes code which can panic we increment the counters beforehand
303            // so that the same index won't be accessed twice, as required by TrustedRandomAccess
304            self.index += 1;
305            // SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()`
306            unsafe {
307                Some((self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i)))
308            }
309        } else {
310            None
311        }
312    }
313
314    #[inline]
315    fn size_hint(&self) -> (usize, Option<usize>) {
316        let len = self.len - self.index;
317        (len, Some(len))
318    }
319
320    #[inline]
321    fn nth(&mut self, n: usize) -> Option<Self::Item> {
322        let delta = cmp::min(n, self.len - self.index);
323        let end = self.index + delta;
324        while self.index < end {
325            let i = self.index;
326            // since get_unchecked executes code which can panic we increment the counters beforehand
327            // so that the same index won't be accessed twice, as required by TrustedRandomAccess
328            self.index += 1;
329            if A::MAY_HAVE_SIDE_EFFECT {
330                // SAFETY: the usage of `cmp::min` to calculate `delta`
331                // ensures that `end` is smaller than or equal to `self.len`,
332                // so `i` is also smaller than `self.len`.
333                unsafe {
334                    self.a.__iterator_get_unchecked(i);
335                }
336            }
337            if B::MAY_HAVE_SIDE_EFFECT {
338                // SAFETY: same as above.
339                unsafe {
340                    self.b.__iterator_get_unchecked(i);
341                }
342            }
343        }
344
345        self.super_nth(n - delta)
346    }
347
348    #[inline]
349    fn next_back(&mut self) -> Option<(A::Item, B::Item)>
350    where
351        A: DoubleEndedIterator + ExactSizeIterator,
352        B: DoubleEndedIterator + ExactSizeIterator,
353    {
354        // No effects when the iterator is exhausted, to reduce the number of
355        // cases the unsafe code has to handle.
356        // See #137255 for a case where where too many epicycles lead to unsoundness.
357        if self.index < self.len {
358            let old_len = self.len;
359
360            // since get_unchecked and the side-effecting code can execute user code
361            // which can panic we decrement the counter beforehand
362            // so that the same index won't be accessed twice, as required by TrustedRandomAccess.
363            // Additionally this will ensure that the side-effects code won't run a second time.
364            self.len -= 1;
365
366            // Adjust a, b to equal length if we're iterating backwards.
367            if A::MAY_HAVE_SIDE_EFFECT || B::MAY_HAVE_SIDE_EFFECT {
368                // note if some forward-iteration already happened then these aren't the real
369                // remaining lengths of the inner iterators, so we have to relate them to
370                // Zip's internal length-tracking.
371                let sz_a = self.a.size();
372                let sz_b = self.b.size();
373                // This condition can and must only be true on the first `next_back` call,
374                // otherwise we will break the restriction on calls to `self.next_back()`
375                // after calling `get_unchecked()`.
376                if sz_a != sz_b && (old_len == sz_a || old_len == sz_b) {
377                    if A::MAY_HAVE_SIDE_EFFECT && sz_a > old_len {
378                        for _ in 0..sz_a - old_len {
379                            self.a.next_back();
380                        }
381                    }
382                    if B::MAY_HAVE_SIDE_EFFECT && sz_b > old_len {
383                        for _ in 0..sz_b - old_len {
384                            self.b.next_back();
385                        }
386                    }
387                    debug_assert_eq!(self.a.size(), self.b.size());
388                }
389            }
390            let i = self.len;
391            // SAFETY: `i` is smaller than the previous value of `self.len`,
392            // which is also smaller than or equal to `self.a.len()` and `self.b.len()`
393            unsafe {
394                Some((self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i)))
395            }
396        } else {
397            None
398        }
399    }
400}
401
402#[stable(feature = "rust1", since = "1.0.0")]
403impl<A, B> ExactSizeIterator for Zip<A, B>
404where
405    A: ExactSizeIterator,
406    B: ExactSizeIterator,
407{
408}
409
410#[doc(hidden)]
411#[unstable(feature = "trusted_random_access", issue = "none")]
412unsafe impl<A, B> TrustedRandomAccess for Zip<A, B>
413where
414    A: TrustedRandomAccess,
415    B: TrustedRandomAccess,
416{
417}
418
419#[doc(hidden)]
420#[unstable(feature = "trusted_random_access", issue = "none")]
421unsafe impl<A, B> TrustedRandomAccessNoCoerce for Zip<A, B>
422where
423    A: TrustedRandomAccessNoCoerce,
424    B: TrustedRandomAccessNoCoerce,
425{
426    const MAY_HAVE_SIDE_EFFECT: bool = A::MAY_HAVE_SIDE_EFFECT || B::MAY_HAVE_SIDE_EFFECT;
427}
428
429#[stable(feature = "fused", since = "1.26.0")]
430impl<A, B> FusedIterator for Zip<A, B>
431where
432    A: FusedIterator,
433    B: FusedIterator,
434{
435}
436
437#[unstable(issue = "none", feature = "trusted_fused")]
438unsafe impl<A, B> TrustedFused for Zip<A, B>
439where
440    A: TrustedFused,
441    B: TrustedFused,
442{
443}
444
445#[unstable(feature = "trusted_len", issue = "37572")]
446unsafe impl<A, B> TrustedLen for Zip<A, B>
447where
448    A: TrustedLen,
449    B: TrustedLen,
450{
451}
452
453// Arbitrarily selects the left side of the zip iteration as extractable "source"
454// it would require negative trait bounds to be able to try both
455#[unstable(issue = "none", feature = "inplace_iteration")]
456unsafe impl<A, B> SourceIter for Zip<A, B>
457where
458    A: SourceIter,
459{
460    type Source = A::Source;
461
462    #[inline]
463    unsafe fn as_inner(&mut self) -> &mut A::Source {
464        // SAFETY: unsafe function forwarding to unsafe function with the same requirements
465        unsafe { SourceIter::as_inner(&mut self.a) }
466    }
467}
468
469// Since SourceIter forwards the left hand side we do the same here
470#[unstable(issue = "none", feature = "inplace_iteration")]
471unsafe impl<A: InPlaceIterable, B> InPlaceIterable for Zip<A, B> {
472    const EXPAND_BY: Option<NonZero<usize>> = A::EXPAND_BY;
473    const MERGE_BY: Option<NonZero<usize>> = A::MERGE_BY;
474}
475
476#[stable(feature = "rust1", since = "1.0.0")]
477impl<A: Debug, B: Debug> Debug for Zip<A, B> {
478    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
479        ZipFmt::fmt(self, f)
480    }
481}
482
483trait ZipFmt<A, B> {
484    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
485}
486
487impl<A: Debug, B: Debug> ZipFmt<A, B> for Zip<A, B> {
488    default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489        f.debug_struct("Zip").field("a", &self.a).field("b", &self.b).finish()
490    }
491}
492
493impl<A: Debug + TrustedRandomAccessNoCoerce, B: Debug + TrustedRandomAccessNoCoerce> ZipFmt<A, B>
494    for Zip<A, B>
495{
496    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497        // It's *not safe* to call fmt on the contained iterators, since once
498        // we start iterating they're in strange, potentially unsafe, states.
499        f.debug_struct("Zip").finish()
500    }
501}
502
503/// An iterator whose items are random-accessible efficiently
504///
505/// # Safety
506///
507/// The iterator's `size_hint` must be exact and cheap to call.
508///
509/// `TrustedRandomAccessNoCoerce::size` may not be overridden.
510///
511/// All subtypes and all supertypes of `Self` must also implement `TrustedRandomAccess`.
512/// In particular, this means that types with non-invariant parameters usually can not have
513/// an impl for `TrustedRandomAccess` that depends on any trait bounds on such parameters, except
514/// for bounds that come from the respective struct/enum definition itself, or bounds involving
515/// traits that themselves come with a guarantee similar to this one.
516///
517/// If `Self: ExactSizeIterator` then `self.len()` must always produce results consistent
518/// with `self.size()`.
519///
520/// If `Self: Iterator`, then `<Self as Iterator>::__iterator_get_unchecked(&mut self, idx)`
521/// must be safe to call provided the following conditions are met.
522///
523/// 1. `0 <= idx` and `idx < self.size()`.
524/// 2. If `Self: !Clone`, then `self.__iterator_get_unchecked(idx)` is never called with the same
525///    index on `self` more than once.
526/// 3. After `self.__iterator_get_unchecked(idx)` has been called, then `self.next_back()` will
527///    only be called at most `self.size() - idx - 1` times. If `Self: Clone` and `self` is cloned,
528///    then this number is calculated for `self` and its clone individually,
529///    but `self.next_back()` calls that happened before the cloning count for both `self` and the clone.
530/// 4. After `self.__iterator_get_unchecked(idx)` has been called, then only the following methods
531///    will be called on `self` or on any new clones of `self`:
532///     * `std::clone::Clone::clone`
533///     * `std::iter::Iterator::size_hint`
534///     * `std::iter::DoubleEndedIterator::next_back`
535///     * `std::iter::ExactSizeIterator::len`
536///     * `std::iter::Iterator::__iterator_get_unchecked`
537///     * `std::iter::TrustedRandomAccessNoCoerce::size`
538/// 5. If `Self` is a subtype of `T`, then `self` is allowed to be coerced
539///    to `T`. If `self` is coerced to `T` after `self.__iterator_get_unchecked(idx)` has already
540///    been called, then no methods except for the ones listed under 4. are allowed to be called
541///    on the resulting value of type `T`, either. Multiple such coercion steps are allowed.
542///    Regarding 2. and 3., the number of times `__iterator_get_unchecked(idx)` or `next_back()` is
543///    called on `self` and the resulting value of type `T` (and on further coercion results with
544///    super-supertypes) are added together and their sums must not exceed the specified bounds.
545///
546/// Further, given that these conditions are met, it must guarantee that:
547///
548/// * It does not change the value returned from `size_hint`
549/// * It must be safe to call the methods listed above on `self` after calling
550///   `self.__iterator_get_unchecked(idx)`, assuming that the required traits are implemented.
551/// * It must also be safe to drop `self` after calling `self.__iterator_get_unchecked(idx)`.
552/// * If `Self` is a subtype of `T`, then it must be safe to coerce `self` to `T`.
553//
554// FIXME: Clarify interaction with SourceIter/InPlaceIterable. Calling `SourceIter::as_inner`
555// after `__iterator_get_unchecked` is supposed to be allowed.
556#[doc(hidden)]
557#[unstable(feature = "trusted_random_access", issue = "none")]
558#[rustc_specialization_trait]
559pub unsafe trait TrustedRandomAccess: TrustedRandomAccessNoCoerce {}
560
561/// Like [`TrustedRandomAccess`] but without any of the requirements / guarantees around
562/// coercions to supertypes after `__iterator_get_unchecked` (they aren’t allowed here!), and
563/// without the requirement that subtypes / supertypes implement `TrustedRandomAccessNoCoerce`.
564///
565/// This trait was created in PR #85874 to fix soundness issue #85873 without performance regressions.
566/// It is subject to change as we might want to build a more generally useful (for performance
567/// optimizations) and more sophisticated trait or trait hierarchy that replaces or extends
568/// [`TrustedRandomAccess`] and `TrustedRandomAccessNoCoerce`.
569#[doc(hidden)]
570#[unstable(feature = "trusted_random_access", issue = "none")]
571#[rustc_specialization_trait]
572pub unsafe trait TrustedRandomAccessNoCoerce: Sized {
573    // Convenience method.
574    fn size(&self) -> usize
575    where
576        Self: Iterator,
577    {
578        self.size_hint().0
579    }
580    /// `true` if getting an iterator element may have side effects.
581    /// Remember to take inner iterators into account.
582    const MAY_HAVE_SIDE_EFFECT: bool;
583}
584
585/// Like `Iterator::__iterator_get_unchecked`, but doesn't require the compiler to
586/// know that `U: TrustedRandomAccess`.
587///
588/// ## Safety
589///
590/// Same requirements calling `get_unchecked` directly.
591#[doc(hidden)]
592#[inline]
593pub(in crate::iter::adapters) unsafe fn try_get_unchecked<I>(it: &mut I, idx: usize) -> I::Item
594where
595    I: Iterator,
596{
597    // SAFETY: the caller must uphold the contract for
598    // `Iterator::__iterator_get_unchecked`.
599    unsafe { it.try_get_unchecked(idx) }
600}
601
602unsafe trait SpecTrustedRandomAccess: Iterator {
603    /// If `Self: TrustedRandomAccess`, it must be safe to call
604    /// `Iterator::__iterator_get_unchecked(self, index)`.
605    unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item;
606}
607
608unsafe impl<I: Iterator> SpecTrustedRandomAccess for I {
609    default unsafe fn try_get_unchecked(&mut self, _: usize) -> Self::Item {
610        panic!("Should only be called on TrustedRandomAccess iterators");
611    }
612}
613
614unsafe impl<I: Iterator + TrustedRandomAccessNoCoerce> SpecTrustedRandomAccess for I {
615    #[inline]
616    unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item {
617        // SAFETY: the caller must uphold the contract for
618        // `Iterator::__iterator_get_unchecked`.
619        unsafe { self.__iterator_get_unchecked(index) }
620    }
621}
622
623trait SpecFold: Iterator {
624    fn spec_fold<B, F>(self, init: B, f: F) -> B
625    where
626        Self: Sized,
627        F: FnMut(B, Self::Item) -> B;
628}
629
630impl<A: Iterator, B: Iterator> SpecFold for Zip<A, B> {
631    // Adapted from default impl from the Iterator trait
632    #[inline]
633    default fn spec_fold<Acc, F>(mut self, init: Acc, mut f: F) -> Acc
634    where
635        F: FnMut(Acc, Self::Item) -> Acc,
636    {
637        let mut accum = init;
638        while let Some(x) = ZipImpl::next(&mut self) {
639            accum = f(accum, x);
640        }
641        accum
642    }
643}
644
645impl<A: TrustedLen, B: TrustedLen> SpecFold for Zip<A, B> {
646    #[inline]
647    fn spec_fold<Acc, F>(mut self, init: Acc, mut f: F) -> Acc
648    where
649        F: FnMut(Acc, Self::Item) -> Acc,
650    {
651        let mut accum = init;
652        loop {
653            let (upper, more) = if let Some(upper) = ZipImpl::size_hint(&self).1 {
654                (upper, false)
655            } else {
656                // Per TrustedLen contract a None upper bound means more than usize::MAX items
657                (usize::MAX, true)
658            };
659
660            for _ in 0..upper {
661                let pair =
662                    // SAFETY: TrustedLen guarantees that at least `upper` many items are available
663                    // therefore we know they can't be None
664                    unsafe { (self.a.next().unwrap_unchecked(), self.b.next().unwrap_unchecked()) };
665                accum = f(accum, pair);
666            }
667
668            if !more {
669                break;
670            }
671        }
672        accum
673    }
674}