Search code examples
rusttraitslifetime

Cannot infer an appropriate lifetime when returning trait object from trait method


I'm trying to make something like immutable Dictionary trait that can be added new items (references) into and used without affecting the previous version. Minimal example:

#[derive(Clone)]
pub struct SetOfValues<'a> {
    value: Vec<&'a i32>,
}

pub trait TheSetAccessor<'b> {
    fn with_additional_values(&self, new_set: Vec<&'b i32>) -> Box<dyn TheSetAccessor<'b>>;
    fn get_from_set(&self, index: usize) -> &i32;
}

impl<'a, 'b : 'a> TheSetAccessor<'b> for SetOfValues<'a> {
    fn with_additional_values(&self, new_set: Vec<&'b i32>) -> Box<dyn TheSetAccessor<'b>> {
        Box::new(SetOfValues { value: new_set } )
    }

    fn get_from_set(&self, index: usize) -> &i32 {
        self.value[index]
    }
}

fn usage() {
    let a = 0;
    let set = SetOfValues {
        value: vec![&a]
    };

    // ...

    let b = 1;
    let extended_set = set.with_additional_values(vec![&a, &b]);

    // ...

    let got_b = extended_set.get_from_set(1);
}

The error message is following:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/test.rs:13:18
   |
13 |         Box::new(SetOfValues { value: new_set } )
   |                  ^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'b` as defined here...
  --> src/test.rs:11:10
   |
11 | impl<'a, 'b : 'a> TheSetAccessor<'b> for SetOfValues<'a> {
   |          ^^
note: ...so that the expression is assignable
  --> src/test.rs:13:39
   |
13 |         Box::new(SetOfValues { value: new_set } )
   |                                       ^^^^^^^
   = note: expected `Vec<&i32>`
              found `Vec<&'b i32>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
  --> src/test.rs:13:9
   |
13 |         Box::new(SetOfValues { value: new_set } )
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Box<(dyn TheSetAccessor<'b> + 'static)>`
              found `Box<dyn TheSetAccessor<'b>>`

As far as I understand, the new SetOfValues should have the lifetime of the passed vector ('b), but this part

first, the lifetime cannot outlive the lifetime 'b as defined here...

as I see, suggests that the new instance of SetOfValues has another lifetime ('static ?) that is supposed to live longer than 'b. I don't quite understand how I can restrict this lifetime. What can I do to make this code work?


Solution

  • This is because dyn Trait is actually dyn Trait + 'static. Thus, dyn TheSetAccessor<'b> is actually dyn TheSetAccessor<'b> + 'static, and cannot contain any non-'static lifetime, so it requires 'b: 'static.

    To relax this bound add a lifetime to the trait: dyn TheSetAccessor<'b> + 'b. Note this may not be the best solution, depending on your use case.

    fn with_additional_values(&self, new_set: Vec<&'b i32>) -> Box<dyn TheSetAccessor<'b> + 'b>;
    

    Playground.