Understanding static lifetimes and trait references


#1

I’m confused about a static lifetime getting introduced and a surprising difference between boxed and ref’d trait objects that I’ve yet to find documentation about.

Playground: https://play.rust-lang.org/?gist=85a7c1189176459f8510d97ddbc7e2b7&version=stable

There’s a little bit of noise here so I can remember the connection to the original code, but otherwise this is minimal reproduction.

The surprising thing is that Case 2 does not work. I understand why Case 3 works even though Case 2 does not (because Box<T> is sugar for Box<T + 'static>), but I don’t understand why it seems to be the impl B for A that is adding an implicit &self + 'static.

Likely forehead slappingly obvious, but feeling rather dense at the moment.


#2

The implied + 'static sugar is not specific to Box, but rather to all trait objects (wherever they appear).

// ...

impl<'a> B for A + 'a {
    fn get_foo(&self) -> Box<C> {
        self.foo()
    }
}

// ...

impl Baz {
    fn as_a_ref<'a>(&'a self) -> &'a (A + 'a) {
        match *self {
            Baz::A1(ref a) => a,
            Baz::A2(ref a) => a,
        }
    }

    // ...
}

Playground

(warning: I only made the quickest changes to get it to compile without much thought; these might need more work)


#3

I’d tried all annotation permutations on as_a_ref, etc., but it’s really this that I’d somehow missed. (For the curious, this change is sufficient alone.) Thanks!

I’d been focused on the ref lifetime so it hadn’t occurred to me that is was impl B for A that was implicitly impl B for A + 'static.


#4

This RFC (and the preceding 599) detail the default object bounds.

One example that’s explicitly missing there (or I missed it) is exactly the case you have: implementing a trait for all trait objects of a given type, which is what impl B for A is doing - A is any trait object of that type (ie &A, Box<A>, Rc<A>, etc). In this case you also get the default 'static bound, which you can change as @ExpHP showed.