Max Heiber
Max Heiber

Reputation: 15502

Why is 'static lifetime required for references returned from methods in an impl for a trait object?

When I don't include the + 'static in this code, rustc complains. Why?

// adapted from rust-analyzer https://github.com/rust-analyzer/rust-analyzer
trait Upcast<T: ?Sized> {
    fn upcast(&self) -> &T;
}

trait DefDatabase {}
trait HirDatabase: Upcast<dyn DefDatabase> {}

struct RootDatabase{}
impl Upcast<dyn DefDatabase> for RootDatabase {
    fn upcast(&self) -> &(dyn DefDatabase + 'static) {
        &*self
    }
}
`impl` item signature doesn't match `trait` item signature
expected `fn(&RootDatabase) -> &(dyn DefDatabase + 'static)`
   found `fn(&RootDatabase) -> &dyn DefDatabase`
the lifetime requirements from the `impl` do not correspond to the
requirements in the `trait`
verify the lifetime relationships in the `trait` and `impl` between the
`self` argument, the other inputs and its output

Why is 'static lifetime required?

It seems like the dyn is essential to the example. rustc does not complain about this impl, which doesn't mention 'static:

impl Upcast<RootDatabase> for RootDatabase {
    fn upcast(&self) -> &Self {
        self
    }
}

Upvotes: 2

Views: 728

Answers (1)

rusty-jules
rusty-jules

Reputation: 46

You need to explicitly state what the lifetime of the Trait Object &'dyn DefDatabase is so that the borrow checker knows what the lifetime of any references held by the struct behind the pointer are. 'static is the default if you do not define it, or it cannot otherwise be determined from the context. See default trait object lifetimes in the rust language reference for more information.

Adding a lifetime to your Upcast trait definition allows you to drop 'static.

trait Upcast<'a, T: ?Sized + 'a> {
    fn upcast(&self) -> &T; // 'a is not necessary here because the generic `T` may or may not be a trait object
}

trait DefDatabase {}

struct RootDatabase{}
impl DefDatabase for RootDatabase {}

impl<'a> Upcast<'a, dyn DefDatabase> for RootDatabase {
    fn upcast(&self) -> &(dyn DefDatabase + 'a) { // now we can replace 'static with 'a
        &*self
    }
}

impl<'a> Upcast<'a, RootDatabase> for RootDatabase {
    fn upcast(&self) -> &Self { // &Self is not a trait object, so no trait object lifetime is necessary
        self
    }
}

Edit:

It should be noted that this lifetime is not the lifetime of the reference to the trait object, which can be declared separately and is the reason for why the lifetime must be disambiguated with parenthesis.

impl<'a> Upcast<'a, dyn DefDatabase> for RootDatabase {
    fn upcast<'b>(&'b self) -> &'b (dyn DefDatabase + 'a) { // 'b is elided
        &*self
    }
}

Upvotes: 3

Related Questions