dyn Trait
lifetimes
As mentioned before, every dyn Trait
has a "trait object lifetime". Even though
it is often elided, the lifetime is always present.
The lifetime is necessary as types which implement Trait
may not be valid everywhere.
For example, &'s String
implements Display
for any lifetime 's
. If you type
erase a &'s String
into a dyn Display
, Rust needs to keep track of that lifetime
so you don't try to print the value after the reference becomes invalid.
So you can coerce &'s String
to dyn Display + 's
, but not dyn Display + 'static
.
Let's look at a couple examples:
#![allow(unused)] fn main() { use core::fmt::Display; fn fails() -> Box<dyn Display + 'static> { let local = String::new(); // This reference cannot be longer than the function body let borrow = &local; // We can coerce it to `dyn Display`... let bx: Box<dyn Display + '_> = Box::new(borrow); // But the lifetime cannot be `'static`, so this is an error bx } }
#![allow(unused)] fn main() { use core::fmt::Display; // This is fine as per the function lifetime elision rules, the lifetime of the // `dyn Display + '_` is the same as the lifetime of the `&String`, and we know // the reference is valid for that long or it wouldn't be possible to call the // function. fn works(s: &String) -> Box<dyn Display + '_> { Box::new(s) } }
When multiple lifetimes are involved
Let's try another example, with a struct
that has more complicated lifetimes.
#![allow(unused)] fn main() { trait Trait {} // We're using `*mut` to make the lifetimes invariant struct MultiRef<'a, 'b>(*mut &'a str, *mut &'b str); impl Trait for MultiRef<'_, '_> {} fn foo<'a, 'b>(mr: MultiRef<'a, 'b>) { let _: Box<dyn Trait + '_> = Box::new(mr); } }
This compiles, but there's nothing preventing either 'a
from being longer than 'b
,
or 'b
from being longer than 'a
. So what's the lifetime of the dyn Trait
? It
can't be either 'a
or 'b
:
#![allow(unused)] fn main() { trait Trait {} #[derive(Copy, Clone)] struct MultiRef<'a, 'b>(*mut &'a str, *mut &'b str); impl Trait for MultiRef<'_, '_> {} // These both fail fn foo<'a, 'b>(mr: MultiRef<'a, 'b>) { let _: Box<dyn Trait + 'a> = Box::new(mr); let _: Box<dyn Trait + 'b> = Box::new(mr); } }
In this case, the compiler computes some lifetime, let's call it 'c
,
such that 'a
and 'b
are both valid for the entirety of 'c
.
That is, 'c
is contained in an intersection of 'a
and 'b
.
Any lifetime for which both 'a
and 'b
are valid over will do:
#![allow(unused)] fn main() { trait Trait {} struct MultiRef<'a, 'b>(*mut &'a str, *mut &'b str); impl Trait for MultiRef<'_, '_> {} // `'c` must be within the intersection of `'a` and `'b` fn foo<'a: 'c, 'b: 'c, 'c>(mr: MultiRef<'a, 'b>) { let _: Box<dyn Trait + 'c> = Box::new(mr); } }
Note that this is not the same as 'a + 'b
-- that is the union
of 'a
and 'b
. Unfortunately, there is no compact syntax
for the intersection of 'a
and 'b
.