Search code examples
rustiteratorlifetime

Make IntoIterator trait implementation lifetime-dynamic


I have this code which works:

impl<T> IntoIterator for Range<T>
where
    T: Display + PartialOrd + 'static,
    RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
    Rev<RangeInclusive<T>>: Iterator<Item = T>,
{
    type Item = T;
    type IntoIter = Box<dyn Iterator<Item = T>>;

    fn into_iter(self) -> Self::IntoIter {
        if self.start > self.end {
            Box::new((self.start..=self.end).rev())
        } else {
            Box::new(self.start..=self.end)
        }
    }
}

I'd like to refactor it to use a dynamic lifetime instead of 'static. However, I cannot figure out how to do this, since I always seem to run into the unconstrained lifetime parameter error:

impl<'a, T> IntoIterator for Range<T>
where
    T: Display + PartialOrd + 'a,
    RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
    Rev<RangeInclusive<T>>: Iterator<Item = T>,
{
    type Item = T;
    type IntoIter = Box<dyn Iterator<Item = T> + 'a>;

    fn into_iter(self) -> Self::IntoIter {
        if self.start > self.end {
            Box::new((self.start..=self.end).rev())
        } else {
            Box::new(self.start..=self.end)
        }
    }
}

Is it possible, do use a dynamic lifetime here?

Orther types


#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Range<T>
where
    T: Display + PartialEq,
{
    start: T,
    end: T,
}

impl<T> Range<T>
where
    T: Display + PartialEq,
{
    pub fn new(start: T, end: T) -> Self {
        Self { start, end }
    }
}

Error

error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
  --> src/lib.rs:56:6
   |
56 | impl<'a, T> IntoIterator for Range<T>
   |      ^^ unconstrained lifetime parameter

Solution

  • You can use Either from either or itertools instead of Box<dyn Iterator> to hold one of two iterators, without the lifetime problems caused by dyn Iterator:

    impl<T> IntoIterator for Range<T>
    where
        T: Display + PartialOrd + 'static,
        RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
    {
        type Item = T;
        type IntoIter = Either<Rev<RangeInclusive<T>>, RangeInclusive<T>>;
    
        fn into_iter(self) -> Self::IntoIter {
            if self.start > self.end {
                Either::Left((self.start..=self.end).rev())
            } else {
                Either::Right(self.start..=self.end)
            }
        }
    }