2
votes

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.

2
While I don't fully understand the error message, it is related to using the same lifetime 'a for everything. Removing it from everywhere except RefHolder<'a> will make it compile, is this whay you want?rodrigo
This code feels very weird to me (struct with a ref to something that can create more refs of itself all with the same lifetime parameters), but I'm beginning to think it might actually be revealing a compiler bug. In particular, if you introduce explicit nesting scopes after each line in main, you still get the error when nothing except c exists in the top scope of main.JMAA
Here's a more complete example showing what I'm looking to do. In essence, I want to store boxed trait objects, and have a method on said trait that will maybe insert a reference to said trait object into a provided list. Though the code is a bit different, I get the same error "borrowed value does not live long enough" play.rust-lang.org/…captaine
@captaine I think it's a very complicated version of stackoverflow.com/questions/32300132/…JMAA

2 Answers

1
votes

This isn't a full answer, because I don't fully understand the source of the error, but essentially I now believe the compiler was cryptically telling you it didn't have enough information to resolve lifetimes.

I find that it's often helpful to remove all lifetimes and add them back in piecemeal only as needed to better understand what's going on, so let's do just that.

Start:

trait Foo {
    fn foo(&self) -> RefHolder;
}

struct SomeType;

impl Foo for SomeType {
    fn foo(&self) -> RefHolder {
        RefHolder(self)
    }
}

struct RefHolder(&(dyn Foo));

// ...etc

Clearly, RefHolder needs a lifetime parameter due to containing a borrow, so adding those in:

trait Foo {
    fn foo(&self) -> RefHolder<'a>;
}

struct SomeType;

impl Foo for SomeType {
    fn foo(&self) -> RefHolder<'a> {
        RefHolder(self)
    }
}

struct RefHolder<'a>(&'a (dyn Foo));

// ...etc

Now foo() needs one. Note, not Foo, just the function.

trait Foo {
    fn foo<'a>(&self) -> RefHolder<'a>;
}

struct SomeType;

impl Foo for SomeType {
    fn foo<'a>(&self) -> RefHolder<'a> {
        RefHolder(self)
    }
}

struct RefHolder<'a>(&'a (dyn Foo));

// ...etc

Now the compiler will tell us much more directly that it can't figure out the lifetime of the return value of foo in the impl. It will say it needs to live as long as 'a (because of the signature) and also as long as the anonymous lifetime of &self (because it's returning self). But it has been told nothing about how those lifetimes relate to each other. So we tell it:

trait Foo {
    fn foo<'a, 's: 'a>(&'s self) -> RefHolder<'a>;
}

struct SomeType;

impl Foo for SomeType {
    fn foo<'a, 's: 'a>(&'s self) -> RefHolder<'a> {
        RefHolder(self)
    }
}

struct RefHolder<'a>(&'a (dyn Foo));

// ...etc

Now everything's happy. What we've done is said 's must be at least as long as 'a (otherwise self could be destroyed before the RefHolder referring to it). The borrow checker check's that's the case when we call it, and all is well with the world.

Before, you started adding lifetimes to the trait Foo, which is a losing battle because you'll end up with an infinite regress as RefHolder suddenly needs to be told how long its dyn Foo will want to be borrowed for, which isn't something you know ahead of time.

Still not exactly sure why this adds up to the exact error you first saw, but I'm happy to have at least partially disentangled this.

1
votes

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();
}