Supertrait for IntoIterator with ExactSizeIterator and lifetime

I'm trying to write a "GPS minimap" renderer. Basically it should turn a gpx struct (from the gpx crate), and generate a png/svg for each of the waypoints, or even interpolated with a given frame rate.

For this, I want to define one trait WaypointProvider with two implementations: SimpleWaypointProvider and InterpolatedWayPointProvider (to be done later). Each of these implementations should basically provide an Iterator to generate Waypoints which are defined like this:

pub struct Waypoint<'track> {
    pub bounding_rect: Rect<f64>,
    pub segment: &'track TrackSegment,
    pub current_waypoint: &'track gpx::Waypoint,
}

The Waypoint's lifetime should be bound to the gpx::Track contained inside e.g. the SimpleWaypointProvider, which I create like this:

pub struct SimpleWaypointProvider {
    track: gpx::Track,
}

impl SimpleWaypointProvider {
    pub fn new(track: &gpx::Track) -> Self {
        Self {
            track: track.clone(),
        }
    }
}

(I've copied the track here to not have to bother with a lifetime in SimpleWaypointProvider). But I don't want to copy each track for each waypoint, because these can go up to several thousands for a longer track (and even more if I do interpolation with a higher framerate).

Inside my main render method I want to receive a trait object of a WaypointProvider so that I can dynamically create one based on CLI arguments (either SimpleWaypointProvider or InterpolatedWayPointProvider):

fn render(waypoint_provider: Box<dyn WaypointProvider>) {
   todo!()
}

I've defined the WaypointProvider trait as follows. It basically should be a trait which returns an ExactSizeIterator (so that I can show a progress bar with the indicatif crate) over Waypoints.

My first attempt is

pub trait WaypointProvider<'track, T>: IntoIterator<Item = Waypoint<'track>, IntoIter = T>
where
    T: ExactSizeIterator<Item = Waypoint<'track>>,
{
}

but I'm now having trouble implementing this. My Iterator is defined like this:

pub struct SimpleWaypointIterator<'track> {
    _provider: &'track SimpleWaypointProvider,
    bounding_rect: Rect<f64>,
    segment: &'track gpx::TrackSegment,

    // The current wp index
    current_wp_index: usize,
}

Implementing the trait gives me all kind of errors, and to be honest, I'm a bit lost about the lifetimes:

impl WaypointProvider<'_, SimpleWaypointIterator<'_>> for SimpleWaypointProvider {}

impl<'provider> IntoIterator for SimpleWaypointProvider {
    type Item = Waypoint<'provider>;
    type IntoIter = SimpleWaypointIterator<'provider>;

    fn into_iter(self) -> Self::IntoIter {
        SimpleWaypointIterator::new(&self)
    }
}

gives me

20 | impl<'provider> IntoIterator for SimpleWaypointProvider {
   |      ^^^^^^^^^ unconstrained lifetime parameter

Basically, neither the Iterator nor the waypoints should live longer than the SimpleWaypointProvider, as that struct holds the track, which everything else references.

I guess it would be quite easy to fix, but I'm a bit lost here.

Thanks in advance :slight_smile:

Perhaps you are looking for this?

impl<'provider> IntoIterator for &'provider SimpleWaypointProvider {
    ...
}
1 Like

Hmm thanks, but why would I have to implement IntoIterator for a reference? I have the SimpleWaypointProvider as a normal owned object and just want to create a for loop on it.

I think I just need to have the lifetime of the Item which is returned from the iterator tied to the initial struct which implements IntoIterator (although I think, it's not even needed for that, it could also have been tied to the Iterator itself).

Well, the _provider reference has to point somewhere. Taking an &SimpleWaypointProvider as the argument to IntoIterator gives us somewhere we can have it point.

The iterator trait does not allow for the item type to hold a reference into the iterator itself. You may want to look up "streaming iterators" or "lending iterators" to read more on this.

As an analogy consider how IntoIterator for Vec<T> has item type of T, whereas IntoIterator for &'a Vec<T> has item type &'a T. Only the IntoIterator on the reference type has any lifetimes involved.