Type definition with a trait: Differences of specifying an explicit lifetime bound?

rust, traits, types

Solution

Answer for new question

You really have two orthogonal cases . I'll tackle the easier one first, the difference of #2 and #3. Comments are inline with what I hope is a representative example:

trait Kind { 
    fn trait_fn(&self) -> u8 { 0 }
}

type CollectionOfKind1<'a> = Vec<&'a (dyn Kind + 'static)>;
type CollectionOfKind2<'a> = Vec<&'a (dyn Kind + 'a)>;

struct Alpha;
impl Kind for Alpha {}

struct Beta<'b> {
    name: &'b str,
}
impl<'a> Kind for Beta<'a> {}

fn main() {
    let name = "world".to_string();

    // Doesn't need/have it's own lifetime.
    let a = Alpha;
    // Has a reference to something with the 'static lifetime.
    let b1 = Beta { name: "hello" };
    // Has a reference to something with the lifetime of `name`,
    // which is less than 'static.
    let b2 = Beta { name: &name[..] };  

    // Our vector is composed of references to
    // things that *might* have a reference themselves!
    let mut c1: CollectionOfKind1 = Vec::new();
    c1.push(&a);
    c1.push(&b1);
    // c1.push(&b2); // error: `name` does not live long enough
    
    let mut c2: CollectionOfKind2 = Vec::new();
    c2.push(&a);
    c2.push(&b1);
    c2.push(&b2); // Hooray
}

Of note here is that the lifetimes don't have to be the same! You could have written:

type CollectionOfKind2<'a, 'b> = Vec<&'a (dyn Kind + 'b)>;

The second thing is the meaning of `trait Foo : 'static`. I'm less sure here, but I wrote this small example:

trait Foo : 'static {}

fn x(a: &Foo) {}

fn main() {
    x(&3u8);
}

This errors during compilation with

the trait `Foo` must be implemented for the cast to the object type `Foo + 'static`

Based on that, I think that `Foo : 'static` is just another way of writing `Foo + 'static`. The main difference I can think of is that it restricts the trait from ever being implemented on a struct with non-`'static` lifetimes:

struct B<'a> {
    b: &'a str,
}

impl<'a> Foo for B<'a> {}

Has the error

declared lifetime bound not satisfied [...] but lifetime parameter must outlive the static lifetime

Original answer

I see you've found this out already, but you may want to update your version of Rust. If I compile your code on the Playpen, I get a suggestion on how to fix it:

error: expected a path on the left-hand side of `+`, not `&'a Kind` [E0178]
type CollectionOfKind<'a> = Vec<&'a Kind + 'a>;
                                ^~~~~~~~
note: perhaps you meant `&'a (Kind + 'a)`? (per RFC 438)
type CollectionOfKind<'a> = Vec<&'a Kind + 'a>;
                                ^~~~~~~~

That references RFC 438, Precedence of Plus.

Problem

I'm having issues understanding the lifetime bound requirements when I use a trait in a type definition. For instance: ``` trait Kind { /* ... */ } type CollectionOfKind<'a> = Vec<&'a Kind>; // => error: explicit lifetime bound required ``` The requirement for lifetime bounds has already been discussed for the case of a trait within a struct (Answer 1, Answer 2). At first, I had problems to apply the approach of "adding the lifetime" here at all, since this does not work: ``` type CollectionOfKind<'a> = Vec<&'a Kind + 'a>; type CollectionOfKind<'a> = Vec<&'a Kind + 'static>; ``` However, this was only a syntax issue, due to the precedence of plus as pointed out by @Shepmaster. Overall, I now have found three ways to specify the lifetime bounds: ``` // Version 1: Adding 'static to the trait itself trait Kind : 'static { /* ... */ } type CollectionOfKind<'a> = Vec<&'a Kind>; ``` ``` // Version 2: Adding 'static to the type definition trait Kind { /* ... */ } type CollectionOfKind<'a> = Vec<&'a (Kind + 'static)>; ``` ``` // Version 3: Adding the lifetime of the reference to the trait object (?) trait Kind { /* ... */ } type CollectionOfKind<'a> = Vec<&'a (Kind + 'a)>; ``` What I don't understand: What is the exact difference between these three cases? My problems: In order to see the differences, I was trying to understand certain points mentioned in the other answers. For instance in the above linked Answer 2, I have found the following hint which I do not fully understand: In this case, the `'static` requires that the underlying object must be a real struct, or a `&'static` reference, but other references won't be allowed. What does it mean that the underlying object must be a "real" struct? How is it possible for a struct to implement the trait without being "real"? Similarly for the quote that @Shepmaster has cited: You have to specify the lifetime two times: once for the lifetime of the reference, and once for the trait object itself, because traits can be implemented for references, and if the underlying object is a reference, you must specify its lifetime as well. To be honest, I don't see why it must be specified twice. I though a trait object is defined by being a reference to an object which implements a certain trait. So it is by definition (?) a reference, and thus, has a lifetime anyways?

Original source

Related problems