简体   繁体   中英

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

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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM