life time of a trait object reference

Viewed 74

With FooClass that implements FooTrait, and Bar that takes the reference of a FooTrait object, the code is working:

trait FooTrait {
    fn print(self);
}

struct FooClass {}

impl FooTrait for FooClass {
    fn print(self) {
        println!("It's a Foo");
    }
}

struct Bar<'a> {
    foo: &'a (dyn FooTrait),
}

impl<'a> Bar<'a> {
    fn new(_foo: &'a (dyn FooTrait)) -> Bar<'a> {
        Bar { foo: _foo }
    }
}

fn main() {
    let foo = FooClass {};
    let bar = Bar::new(&foo);
    println!("hello world");
}

But for

struct Bar<'a> {
    foo: &'a (dyn FooTrait),
}

impl<'a> Bar<'a> {
    fn new(_foo: &'a (dyn FooTrait)) -> Bar<'a> {
        Bar { foo: _foo }
    }
}

I have also seen they are written as

struct Bar<'a> {
    foo: &'a (dyn FooTrait + 'a),
}

impl<'a> Bar<'a> {
    fn new(_foo: &'a (dyn FooTrait + 'a)) -> Bar<'a> {
        Bar { foo: _foo }
    }
}

I think I understand that struct Bar<'a> { foo: &'a (dyn FooTrait + 'a), } says, foo should live at least as long as Bar so that reference stay valid. But this explains only Bar<'a> and foo: &'a, that I don't see what does + 'a do after dyn FooTrait in the parentheses. Also, removing +'a doesn't seems to break the program. Can someone explain to me the purpose?

1 Answers

The + 'a says that not only does the concrete type need to implement FooTrait, but it also may not itself contain references that live shorter than 'a.

Consider:

struct FooWithRefs<'b> { reff: &'b i32, }
impl <'b> FooTrait for FooWithRefs<'b> { ... }

fn main() {
    let i = 100;
    let foo = FooWithRefs { reff: &i };
    let bar = Bar::new(&foo);
    println!("hello world");
}

The compiler needs to know that not only does foo not disappear during the lifetime of bar, but also that nothing foo itself references goes out of scope. The + 'a condition ensures this.

(I can't think of a way to write an example that violates this condition that won't also violate the mere creation of the FooWithRefs though.)

I don't know the exact semantics of omitting the lifetime bound, though. Trying it out suggests that it implicitly uses the same bound.

See it on the playground

Related