This hits on a dyn Trait
's "lifetime of a applicability" , which you may not be aware of. Every dyn Trait
has an associated lifetime, so the type is really dyn Trait + 'lifetime
. It has a lot of special behavior (like custom elision rules), in part so that you usually "don't have to think about it". But all the special casing makes it quite complex, and I think you're just hitting on an unfortunate combination of special cases.
It's nothing I'd expect a new comer to know their way around. I believe the TL;DR fix is:
-fn other(_: &[&mut dyn T1]) {}
+fn other<'app>(_: &[&mut (dyn T1 + 'app)]) {}
Though I'd be curious to hear how you ran into this, if you were trying something practical.
Now I'll walk through what I believe is going on.
fn other(_: &[&mut dyn T1]) {}
Here, the dyn T1
is behind a reference, so the default lifetime of applicability is taken to be the same as that of the reference. The function ends up behaving like so:
fn other<'out, 'inner>(_: &'out [&'inner mut (dyn T1 + 'inner)]) {}
Then in main
:
let mut b: Vec<Box<dyn T1>> = vec![];
Since this is in a function body, the lifetime of applicability is inferred . The inference is influenced by the call to other
-- if you don't call other
(e.g. you just call Drop
), the inference may be different (and hence may compile).
But with other
, the end result is that you end up with the contents of the Vec
being borrowed for the rest of their lifetime. This happens whenever you have a &'a mut SomeThing<'a>
and is an anti-pattern -- you can never use a struct borrowed like this again. In this case, the Drop
implementation is trying to "use" the structs within the Vec
again, and you get an error.
There's a lot of potential subtleties here that I won't pursue.
So why does Any
work?
Any
has a 'static
bound. If you add such a bound to T1
:
trait T1: 'static {}
Then the default lifetime of applicability becomes 'static
, and this:
fn other(_: &[&mut (dyn T1)]) {}
Acts like this:
fn other<'out, 'inner>(_: &'out [&'inner mut (dyn T1 + 'static)]) {}
Playground.
This hints at another way to get things to compile: Leave the bound off the trait, but explicitly require a 'static
lifetime of applicability on other
:
fn other(_: &[&mut (dyn T1 + 'static)]) {}
Can we avoid requiring 'static
? A first attempt is to keep the signature of other
and adjust the Vec
(moving where we require 'static
to see if it's viable for other
):
fn other(_: &[&mut dyn T1]) {}
// &'out[&'inner mut (dyn T1 + 'inner)]
fn main() {
let mut b: Vec<Box<dyn T1 + 'static>> = vec![];
But because the lifetime of applicability is implied to be the same as the reference, we get:
error[E0597]: `b` does not live long enough
--> src/main.rs:9:35
|
8 | let mut b: Vec<Box<dyn T1 + 'static>> = vec![];
| -------------------------- type annotation requires that `b` is borrowed for `'static`
However, we can break the equality by giving the lifetime of applicability an explicitly fresh lifetime parameter:
fn other<'app>(_: &[&mut (dyn T1 + 'app)]) {}
And in fact, once the equality is broken, the lifetime in the Box
need no longer be 'static
either, and we can let it be inferred again.
This version allows non-'static
implementers of the trait.
impl<'a> T1 for &'a A {}
fn other<'app>(_: &[&mut (dyn T1 + 'app)]) {}
fn main() {
let a = A {};
let mut b: Vec<Box<dyn T1>> = vec![Box::new(&a)];
And that's the diff I suggested at the top.
Again, all pretty obscure stuff that I wouldn't expect someone to know when learning Rust.