Why is my lifetime parameter not working in a Box?

I am pretty new to Rust and I am working on a crate that will provide an API for using a Discord bot to run drafts of various things (i.e. fantasy sports, Pokemon leagues etc). I have a public trait, DraftItem, which end users can implement on whatever struct they create to hold information about a selection in their draft (a football player, a Pokemon, etc). This is the trait definition:

pub trait DraftItem {
fn name(&self);
}

It's very simple so far, it might need some other methods eventually but for now it just needs to return a name that can be sent via a Discord message.
I also have an ActivePlayer struct that holds DraftItems that the player has queued up or locked in:

pub struct ActivePlayer {
    queue: Vec<Box<dyn DraftItem>>,
    ...
}

impl ActivePlayer {
    pub fn add_to_queue<'a, T>(&mut self, item: T)
    where
        T: DraftItem + 'a,
    {
        self.queue.push(Box::new(item));
    }
}

This code won't compile, giving the error E0310: the parameter type T may not live long enough. I am not super comfortable with either trait bounds or lifetimes, so I'm struggling to understand why my lifetime annotation isn't working properly. The compiler suggests annotating T with 'static but I definitely don't want the DraftItems to live for the whole runtime. Do I need to annotate my DraftItem or ActivePlayer definitions with lifetimes? Is the compiler error specifically because I'm trying to put the DraftItem in a box, or is this a more general problem with the design of my code?

T: 'static doesn't mean T will live for the lifetime of the app; it means it can if needed. This is a requirement of anything that goes into Box, since Box could live for the lifetime of the app. Correction: it's the Box<dyn DraftItem>; see @quinedot's answer.

If you define a struct or enum with no lifetime args and no type args, then it will meet the 'static bound.

2 Likes

The Box<dyn Trait> is implicitly a Box<dyn Trait + 'static>. That's where the 'static requirement is coming from.

T: 'static doesn't mean values of T stick around for the whole runtime, it means that that the validity of the type T isn't limited by any lifetime, for example by containing some non-'static reference. See misconception #2 here.

String: 'static for example, but you can create and destroy one in any function body; they don't stick around forever.

Your choices are to infect everything with lifetimes and use something like a Box<dyn Trait + 'a>, or to put the compiler-suggested 'static bound on types which you're going to coerce to Box<dyn Trait>.

Probably you want the latter option, especially when starting out in Rust. This may mean you have to convert a borrowed &str into an owned String or similar too. This is generally perfectly okay and probably what you want anyway.

5 Likes

thank you, that's a great explanation. I was definitely misunderstanding 'static. I'll just use the compiler suggestion.

Thanks for the quick response, it makes sense that 'static wouldn't actually force something to live for the whole lifetime of the app.