davenportw15
davenportw15

Reputation: 157

Mix lifetimes of references in Vec

I am attempting to generate a Vec<&'b Color> from Vec<&'a Color>:

impl <'a> Canvas<'a> {
    pub fn modify<'b>(&self, update: Update) -> Canvas<'b> {
        let x  = update.x;
        let y  = update.y;
        let colors: Vec<&'b Color> = self.colors.iter().enumerate().map(move |(index, color)| {
                if index == self.width * y + x { update.color } else { color }
            })
            .collect();
        Canvas { width: self.width, height: self.height, colors: colors }
    }
}

However, I get the following error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/canvas.rs:51:50
   |
51 |         let colors: Vec<&'b Color> = self.colors.iter().enumerate().map(move |(index, color)| {
   |                                                  ^^^^

How can I create a Vec of colors, all but one with lifetime 'a, and the remaining with lifetime 'b?

If needed, the relevant definitions are as follows:

#[derive(Debug, PartialEq, Eq)]
pub enum Color {
    White,
    Red,
    Blue,
    Green,
    Purple,
    Orange
}

pub struct Update<'a> {
    color: &'a Color,
    x: usize,
    y: usize
}

pub struct Canvas<'a> {
    width: usize,
    height: usize,
    colors: Vec<&'a Color>
}

Upvotes: 0

Views: 1462

Answers (1)

Shepmaster
Shepmaster

Reputation: 431599

How can I create a Vec of colors, all but one with lifetime 'a, and the remaining with lifetime 'b?

You cannot. Lifetimes are generics, and just like it doesn't make sense to have "a Vec<T>, all but one of T is the type String, and the remaining T is the type bool", it doesn't make sense to do so with lifetimes.

What you can do is unify the lifetimes:

impl<'a> Canvas<'a> {
    pub fn modify(&self, update: Update<'a>) -> Canvas<'a> {
        let x = update.x;
        let y = update.y;
        let colors = self.colors
            .iter()
            .enumerate()
            .map(move |(index, color)| if index == self.width * y + x {
                     update.color
                 } else {
                     color
                 })
            .collect();
        Canvas {
            width: self.width,
            height: self.height,
            colors: colors,
        }
    }
}

Here, we've said that the update.color lifetime and the lifetime of the contained references have an intersection, and that's what the lifetime of the result will be.


Another common solution for a heterogenous collection of a fixed set of types is to introduce an enum with variants for each one:

pub enum Thing<'a, 'b>  {
    A(&'a Color),
    B(&'b Color),
}

impl<'a> Canvas<'a> {
    pub fn modify<'b>(&self, update: Update<'b>) -> Vec<Thing<'a, 'b>> {
        let x = update.x;
        let y = update.y;
        self.colors
            .iter()
            .enumerate()
            .map(move |(index, color)| if index == self.width * y + x {
                     Thing::B(update.color)
                 } else {
                     Thing::A(color)
                 })
            .collect()
    }
}

Upvotes: 3

Related Questions