Tunococ
Tunococ

Reputation: 195

How to create an `Iterable` trait for references in Rust?

I'm trying to create a trait that captures the iter function in slice as well as VecDeque, BTreeMap and HashMap. I'd like the implementer of this trait to be able to specify and implement their own iterator type, but it looks like this iterator type must have a lifetime argument, and that cannot be given as an associated type.

In more detail, here's what I wish was possible in Rust:

trait RefIterable<T>
where for<'a> (T: 'a) => (Self::Iter<'a>: Iterator<Item = &'a T>)
{
  type Iter; // Has kind (lifetime -> type)
  fn refs<'a>(&'a self) -> Self::Iter<'a>
}

If this was possible, the implementation could look like this

impl RefIterable<T> for Vec<T> {
  type Iter<'a> = std::slice::Iter<'a, T>; // This is not valid Rust code.
  fn refs<'a>(&'a self) -> std::slice::Iter<'a, T> {
    self.as_slice().iter()
  }
}

I'm still relatively new to Rust, so I'm asking if there's already a way to do this that I'm not aware of, or if there's a nice workaround for this situation. I'd imagine that this situation is not very rare.

(Using Box<dyn 'a + Iterator<Item = &'a T>> is my current workaround, but that prevents some optimization from happening.)

Edit:

EvilTak's answer is probably the best thing we can do right now. The ability to combine all possible lifetimes together with the condition T: 'a into one unparametrized trait seems to be unsupported by Rust as of today.

Upvotes: 5

Views: 483

Answers (1)

EvilTak
EvilTak

Reputation: 7579

Add the lifetime parameter to the trait instead, which allows you to use it in the associated type Iter's bound:

trait RefIterable<'a> {
    type Item: 'a;
    type Iter: Iterator<Item = &'a Self::Item>; // Has kind (lifetime -> type)
    fn refs(&'a self) -> Self::Iter;
}

The Item: 'a bound is required to let the compiler know that the references (&'a Self::Item) do not outlive the type (Self::Item).

I have modified RefIterable to make it follow Iterator's convention of using an associated type to specify the type of the items that are iterated over for the same reason as the one behind Iterator's usage of an associated type.

Implementations are pretty straightforward:

impl<'a, T: 'a> RefIterable<'a> for Vec<T> {
    type Item = T;
    type Iter = std::slice::Iter<'a, T>;
    
    fn refs(&'a self) -> std::slice::Iter<'a, T> {
        self.as_slice().iter()
    }
}

Playground

Upvotes: 3

Related Questions