tradinggy
tradinggy

Reputation: 1251

Rust iterator generics question, can't compile

I'm trying to use generics for iterators in traits and am having a hard time understanding why it does not compile.

struct B {
    values: Vec<(i64, i64)>,
}

pub trait IterableCustom {
    fn get_iterator<'a, I>(&self) -> I
    where
        I: Iterator<Item = &'a (i64, i64)>;
}

impl IterableCustom for B {
    fn get_iterator<'a, I>(&self) -> I
    where
        I: Iterator<Item = &'a (i64, i64)>,
    {
        let rev = self.values.iter().rev();
        rev
    }
}

It throws an error:

error[E0308]: mismatched types
  --> src/lib.rs:17:9
   |
12 |     fn get_iterator<'a, I>(&self) -> I
   |                         -            - expected `I` because of return type
   |                         |
   |                         this type parameter
...
17 |         rev
   |         ^^^ expected type parameter `I`, found struct `Rev`
   |
   = note: expected type parameter `I`
                      found struct `Rev<std::slice::Iter<'_, (i64, i64)>>`

For more information about this error, try `rustc --explain E0308`.

Playground

Upvotes: 1

Views: 198

Answers (1)

kotatsuyaki
kotatsuyaki

Reputation: 1641

The only thing we know about I is that I implements Iterator<Item = &'a (i64, i64)>, so I can be any iterator type satisfying the trait bound. Therefore, the type of rev does not always equal to I.

Without GAT or existential types (which are not in stable Rust yet), currently the best way to rewrite it is to return a boxed trait object.

struct B {
    values: Vec<(i64, i64)>,
}

pub trait IterableCustom {
    fn get_iterator(&self) -> Box<dyn Iterator<Item = &(i64, i64)> + '_>;
}

impl IterableCustom for B {
    fn get_iterator(&self) -> Box<dyn Iterator<Item = &(i64, i64)> + '_> {
        let rev = self.values.iter().rev();
        Box::new(rev)
    }
}

Nightly solutions

With generic associated types, the code can be rewritten as such.

#![feature(generic_associated_types)]

struct B {
    values: Vec<(i64, i64)>,
}

pub trait IterableCustom {
    type Iter<'a>: Iterator<Item = &'a (i64, i64)>
    where
        Self: 'a;
    fn get_iterator<'a>(&'a self) -> Self::Iter<'a>;
}

impl IterableCustom for B {
    type Iter<'a> = std::iter::Rev<std::slice::Iter<'a, (i64, i64)>>;
    fn get_iterator<'a>(&'a self) -> Self::Iter<'a> {
        self.values.iter().rev()
    }
}

With existential types, the overly verbose std::iter::Rev<...> type can be further reduced as such.

type Iter<'a> = impl Iterator<Item = &'a (i64, i64)>
where
    Self: 'a;

Upvotes: 4

Related Questions