genericsrustlifetime

Rust iter-map-collect pattern with lifetimes


I'm trying to implement a shortcut for Vec.iter().map(...).collect() pattern that I use pretty often. At first glance, it doesn't look difficult:

pub trait IterMap<T> {
    fn mapcollect<F, U>(&self, f: F) -> Vec<U>
    where
        F: Fn(&T) -> U;
}

impl<T> IterMap<T> for Vec<T> {
    fn mapcollect<F, U>(&self, f: F) -> Vec<U>
    where
        F: Fn(&T) -> U,
    {
        self.iter().map(f).collect()
    }
}

It works like a charm for simple cases:

let v = vec![5; 5];
let v_2 = v.mapcollect(|x| x * x);

However, if lifetimes are involved, it breaks:

let v: Vec<Vec<i32> = ...; // define 2D-vector here
let v_slice: Vec<&[i32]> = v.mapcollect(|x| x.as_slice());

The reason being that Rust is unable to link the lifetime of v_slice to the one of v through my mapcollect() abstraction.

Of course, I can implement another mapcollect_with_lifetime(...) with proper lifetime annotations, but this approach has several disadvantages. Is there a way to help Rust figure out the lifetimes automatically, as the original iter().map(...).collect() does?


Solution

  • You don't need a second implementation at all, you can do this with a single, properly implemented trait and implementation. This is because adding the lifetimes to your implementation is how you help rust automatically figure this out for the caller.

    pub trait IterMap<'a, T: 'a> {
        fn mapcollect<F, U>(&'a self, f: F) -> Vec<U>
        where
            F: Fn(&'a T) -> U,
            U: 'a;
    }
    
    impl<'a, T: 'a> IterMap<'a, T> for Vec<T> {
        fn mapcollect<F, U>(&'a self, f: F) -> Vec<U>
        where
            F: Fn(&'a T) -> U,
            U: 'a,
        {
            self.iter().map(f).collect()
        }
    }
    
    fn main() {
        let v = vec![5; 5];
        let v_2 = v.mapcollect(|x| x * x);
    
        let v: Vec<Vec<i32>> = vec![vec![1, 2, 3], vec![4, 5, 6]];
    
        let v_slice: Vec<&[i32]> = v.mapcollect(|x| x.as_slice());
    }