Mismatched type when including lifetimes to struct

Hey everyone,

I'm new to Rust and am trying to get a better grasp of the language by applying it on design patterns.

I came across an error which I couldn't solve, as shown with the simplified code below:

pub trait MyTrait {
    fn get_attr(&self) -> &mut Vec<&Box<dyn MyTrait>>;
    fn my_method(&self);  // Makes use of `self.get_attr()`
}

pub struct MyStruct<'a> {
    my_attr: Vec<&'a Box<dyn MyTrait>>,
}
impl<'a> MyStruct<'a> {
    pub fn new() -> Self {
        MyStruct {
            my_attr: Vec::new(),
        }
    }
}
impl<'a> MyTrait for MyStruct<'a> {
    fn get_attr(&self) -> &mut Vec<&Box<dyn MyTrait>> {
        &mut self.my_attr
    }

    fn my_method(&self) {
        ...
    }

The borrow checker instructed me to add a lifetime specifier to my_attr of MyStruct, which I did.

However, that led to the error below, which I do not know how to resolve:

error[E0308]: mismatched types
  --> src/sample.rs:17:9
   |
17 |         &mut self.my_attr
   |         ^^^^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected mutable reference `&mut Vec<&Box<(dyn MyTrait + 'static)>>`
              found mutable reference `&mut Vec<&'a Box<(dyn MyTrait + 'static)>>`
note: the anonymous lifetime defined here...
  --> src/sample.rs:16:17
   |
16 |     fn get_attr(&self) -> &mut Vec<&Box<dyn MyTrait>> {
   |                 ^^^^^
note: ...does not necessarily outlive the lifetime `'a` as defined here
  --> src/sample.rs:15:6
   |
15 | impl<'a> MyTrait for MyStruct<'a> {
   |      ^^

Appreciate it if someone can explain what I'm doing incorrectly here, or suggest a better way to go about my program structure.

Much thanks in advance!

This function signature contains elided lifetimes. The lifetime elision rules expand it into

fn get_attr<'t>(&'t self) -> &'t mut Vec<&'t Box<dyn MyTrait>>;

('t is an arbitrary name I picked to be different from 'a.) But the Vec you are returning has elements whose lifetime is 'a, not 't. That's the mismatch the compiler is telling you about.

Sometimes, lifetimes that are longer are acceptable substitutes for lifetimes that are shorter (covariance) but inside of an &mut, lifetimes must match exactly (invariance). This is because an &mut allows writing, and it would be unsound if you could insert into the vector references that had a shorter lifetime.

Another problem, unrelated to the above, is that you cannot obtain an &mut reference starting from an & reference. Thus, get_attr must have &mut self. With that change, and adding the 'a lifetime to the trait, this program compiles:

pub trait MyTrait<'a> {
    fn get_attr(&mut self) -> &mut Vec<&'a Box<dyn MyTrait<'a>>>;
    fn my_method(&self);  // Makes use of `self.get_attr()`
}

pub struct MyStruct<'a> {
    my_attr: Vec<&'a Box<dyn MyTrait<'a>>>,
}
impl<'a> MyStruct<'a> {
    pub fn new() -> Self {
        MyStruct {
            my_attr: Vec::new(),
        }
    }
}
impl<'a> MyTrait<'a> for MyStruct<'a> {
    fn get_attr(&mut self) -> &mut Vec<&'a Box<dyn MyTrait<'a>>> {
        &mut self.my_attr
    }

    fn my_method(&self) {
        //...
    }
}

However, I should warn you: it is often (not always, but often) a sign of a mistake when all your data structures have a lifetime on them — because references greatly restrict what you can actually do with the structure. You should reconsider whether MyStruct should actually contain references, or whether instead it should just own its data:

pub struct MyStruct {
    my_attr: Vec<Box<dyn MyTrait>>,
}

This way, it's actually possible to, for example, construct a MyStruct that contains more, and return the whole tree from a function; with references, you'd find that impossible because they hold borrowed data. And possible to mutate the “attributes”, too.

6 Likes

Moreover, just as you should prefer &[T] and &str over &Vec<T> and &String, you should prefer &dyn Trait over &Box<dyn Trait>. [1]


  1. More explicitly it's &'x dyn Trait + 'x vs. &'x Box<dyn Trait + 'static>. ↩︎

3 Likes

Thank you very much @kpreid and @quinedot for your instructive replies.

Apart from your explanation on invariance @kpreid, the key line to me was the following:

as it tells the compiler that both these lifetimes are one and the same.


In my actual code, I'm implementing the Command pattern so there is another attribute (command) in MyStruct that own the concrete traits borrowed by command_history:

use crate::Command;
use std::collections::HashMap;

pub trait Invoker<'a> {
    fn get_command(&mut self) -> &mut HashMap<&'a str, dyn Command>;
    fn get_command_history(&mut self) -> &mut [&'a dyn Command];
    fn run_command(&self, key: &str) {
        if let Some(&cmd) = self.get_command().get(key) {
            cmd.execute();
            self.get_command_history().push(cmd);
        } else { }  // Handle key error
    }
}

pub struct MyStruct<'a> {
    command: HashMap<&'a str, dyn Command>,
    command_history: [&'a dyn Command],
}
impl<'a> MyStruct<'a> {
    ...
}
impl<'a> Invoker<'a> for MyStruct<'a> {
    ...
}

Once again, thanks for the help!

You can't (in plain Rust[1]) put borrows of data in a struct in another part of the same struct. (Or rather, you can, but the lifetimes will quickly become unusably jammed up.) You'll find that you will need to do something else — such as storing a key/index instead of a reference, or arranging so the borrowed data lives elsewhere.


  1. The ouroboros library lets you, but that's often more inconvenient and expensive than alternatives; it shouldn't be the first thing you reach for. ↩︎

1 Like

Gotcha! Thanks for the tip @kpreid!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.