I'm trying to understand why I receive an error when attempting to compile the following code
trait Foo<'a> {
fn foo(&'a self) -> RefHolder<'a>;
}
struct SomeType;
impl<'a> Foo<'a> for SomeType {
fn foo(&'a self) -> RefHolder<'a> {
RefHolder(self)
}
}
struct RefHolder<'a>(&'a (dyn Foo<'a>));
struct Container<'a> {
pub objs: Vec<Box<dyn Foo<'a>>>,
}
fn main() {
let mut c = Container { objs: Vec::new() };
c.objs.push(Box::from(SomeType {}));
let o = &c.objs[0].as_ref();
let x = o.foo();
}
I receive the error
error[E0597]: `c.objs` does not live long enough
--> src/main.rs:21:14
|
21 | let o = &c.objs[0].as_ref();
| ^^^^^^ borrowed value does not live long enough
22 | let x = o.foo();
23 | }
| -
| |
| `c.objs` dropped here while still borrowed
| borrow might be used here, when `c` is dropped and runs the destructor for type `Container<'_>`
I'm confused as to why c.objs is still borrowed at the end of main. It's my understanding that x will be dropped first, followed by o, which means no references to c should exist at that point, allowing c to finally be dropped without issue.
When the compiler says that the borrowed value does not live long enough what it actually means is that the lifetime of this value is used beyond its range. The compiler phrases it that way because that is the most usual reason of lifetime violation, but your code happens to be more complicated because of how the lifetime are deduced.
The key line in your code is:
let x = o.foo();
With it, the code compiles fine. Actually it is equivalent to this one
let o : &dyn Foo = c.objs[0].as_ref();
Foo::foo(o);
(the extra & is not needed, but that is not important).
Now the what is the life time of that o reference? Well, since it is initialized from a Box::as_ref() and that is defined as (lifetime unelided):
fn as_ref<'s>(&'s self) -> &'s T
it is the same life time as that of the Box itself, that it is taken from the vector, using the Index trait... so it will eventually be the lifetime of c.objs.
Now, because of the way your trait is defined:
fn foo(&'a self) -> RefHolder<'a>
the returned trait has that very same lifetime. And since every generic in your code uses the same lifetime, so is the lifetime of Container<'a>.
That is, the concrete lifetime of c: Container<'?> is the lifetime of one of its members. That is analogous to a self-referential struct and it is not allowed.
Your code can be made to compile easily simply by removing all lifetimes generics except those that are actually required:
trait Foo {
fn foo<'a>(&'a self) -> RefHolder<'a>;
}
struct SomeType;
impl Foo for SomeType {
fn foo<'a>(&'a self) -> RefHolder<'a> {
RefHolder(self)
}
}
struct RefHolder<'a>(&'a (dyn Foo));
struct Container {
pub objs: Vec<Box<dyn Foo>>,
}
fn main() {
let mut c = Container { objs: Vec::new() };
c.objs.push(Box::from(SomeType {}));
let o : &dyn Foo = c.objs[0].as_ref();
let x = o.foo();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With