Skip to main content

core/iter/adapters/
zip.rs

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