Search code examples
rusttraitsassociated-types

Rust type error when implementing Iterator


I'm trying to generalize .split from slices to iterators, and I can't seem to get the types right. This is partially because I'm not very experienced with Rust, and partially because the error message is weirdly vague for what I've seen from Rust. I've included what I believe are the relevant snippets, but there's also a trait Splittable and an implementation for it. The errors (I believe the second error is caused by the first, please tell me if that's not correct):

struct Split<I: Iterator, P: Fn(&I::Item) -> bool> {
    iter: I,
    pred: P,
}

impl<I: Iterator, P: Fn(&I::Item) -> bool> Iterator for Split<I, P> {
    type Item = Vec<I::Item>;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .cloned()
            .position(|x| (self.pred)(&x))
            .map(|i| self.iter.take(i))
    }
}
error[E0271]: type mismatch resolving `<I as std::iter::Iterator>::Item == &_`
  --> src/main.rs:13:14
   |
13 |             .cloned()
   |              ^^^^^^ expected associated type, found reference
   |
   = note: expected associated type `<I as std::iter::Iterator>::Item`
                    found reference `&_`
   = note: consider constraining the associated type `<I as std::iter::Iterator>::Item` to `&_` or calling a method that returns `<I as std::iter::Iterator>::Item`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

error[E0599]: no method named `position` found for struct `std::iter::Cloned<I>` in the current scope
   --> src/main.rs:14:14
    |
14  |               .position(|x| (self.pred)(&x))
    |                ^^^^^^^^ method not found in `std::iter::Cloned<I>`
    |
    = note: the method `position` exists but the following trait bounds were not satisfied:
            `<I as std::iter::Iterator>::Item = &_`
            which is required by `std::iter::Cloned<I>: std::iter::Iterator`

Solution

  • The particular error message you are getting is a little misleading. What it is really complaining about is that std::slice::cloned requires an iterator over references, which you can see from its definition:

    fn cloned<'a, T>(self) -> Cloned<Self>
    where
        Self: Iterator<Item = &'a T>,
        T: 'a + Clone, 
    

    It requires an iterator over references to items that implement Clone (and the description states that it is useful for converting iterators over &T to iterators over T).

    I suspect, however, that you are not trying to clone the items that are being iterated, rather that you are trying to clone the iterator itself. For that you would use the clone method, rather than cloned, and you would have to add a bound to ensure that the iterator is clonable, like this:

    struct Split<I, P>
    where
        I: Iterator + Clone,
        P: Fn(&I::Item) -> bool
    

    There are a few other problems in your code:

    • You are borrowing self multiple times, to access the iter and pred.
    • You are effectively moving self.iter into the closure inside map
    • take consumes the iterator you call it on, so even if you refactor to avoid the above two problems, it still will not compile.

    If you did intend to restrict your implementation to iterators over references, you could define your bounds like this:

    struct Split<'a, I, T, P>
    where
        I: Iterator<Item=&'a T>,
        T: 'a + Clone,
        P: Fn(&I::Item) -> bool
    

    Then you would be able to use cloned.