Skip to content

What guarantees does core::iter::Repeat give around observing calls to clone? #81292

Closed
@lopopolo

Description

@lopopolo

Currently, core::iter::Repeat::nth calls core::iter::Repeat::next n times.

This allows a struct with interior mutability or other global state and a custom clone implementation to observe the number of times it has been cloned.

use std::sync::atomic::{AtomicUsize, Ordering};

static COUNTER: AtomicUsize = AtomicUsize::new(0);

struct X;

impl Clone for X {
    fn clone(&self) -> Self {
        COUNTER.fetch_add(1, Ordering::SeqCst);
        Self
    }
}

fn main() {
    let mut iter = core::iter::repeat(X);
    for _ in 0..10_000 {
        let _ = iter.next();
    }
    println!("counter: {}", COUNTER.load(Ordering::SeqCst));
    let _ = iter.nth(555);
    println!("counter: {}", COUNTER.load(Ordering::SeqCst));
}

Currently, this program outputs:

counter: 10000
counter: 10556

Would it be possible to override the implementations of some Iterator methods that have default implementations without breaking any documented contract?

impl<A: Clone> Iterator for Repeat<A> {
    type Item = A;

    #[inline]
    fn next(&mut self) -> Option<A> {
        Some(self.element.clone())
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (usize::MAX, None)
    }

    #[inline]
    fn advance_by(&mut self, n: usize) -> Result<(), usize> {
        // Advancing an infinite iterator of a single element is a no-op.
        let _ = n;
    }

    #[inline]
    fn nth(&mut self, n: usize) -> Option<T> {
        let _ = n;
        Some(self.element.clone())
    }

    fn last(self) -> Option<T> {
        loop {}
    }

    fn count(self) -> usize {
        loop {}
    }
}

impl<A: Clone> DoubleEndedIterator for Repeat<A> {
    #[inline]
    fn next_back(&mut self) -> Option<A> {
        Some(self.element.clone())
    }

    #[inline]
    fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
        // Advancing an infinite iterator of a single element is a no-op.
        let _ = n;
    }

    #[inline]
    fn nth_back(&mut self, n: usize) -> Option<T> {
        let _ = n;
        Some(self.element.clone())
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-iteratorsArea: IteratorsI-slowIssue: Problems and improvements with respect to performance of generated code.T-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions