This is the fourth part of the Learn You a Rust for Great Good! tutorial explainer series. If you’re coming to the series for the first time, I recommend starting at the first post linked above.

For those who’ve subscribed to my RSS feed (and whose periodic requests I have seen passing by in vain in my nginx log), apologies for not writing anything sooner. I know, the last time I wrote stuff was April. But don’t worry, I’ve come back after procrastinating forever working on my personal audio project with some more irksome lifetime problems, and how to solve them.

Last time, in LYaR III, we looked at the most basic kinds of lifetime problems the enterprising new Rustacean faces: storing a reference in a struct, and dealing with more than one reference in a function signature. This time, I’m going to give you some examples of lifetime-related quarrels I’ve personally had with the Rust compiler, and how I and it came to an amicable compromise.

Combining objects and contexts

Last time, we talked about how objects’ lifetimes are linked to their containing block, and how they are allocated & deallocated in order. Here’s a refresher example:

/* note: in real life, these objects wouldn't actually be
   allocated and deallocated, because they're numbers with
   no `Drop` impl. however, just pretend they are in this
   example. */
fn main() {
    let a = 3; /* a created */
    let b = 4; /* b created */
    let c = 5; /* c created */
}
// main()s scope ends, killing all the variables inside it...
// c goes away, as it was declared last
// b goes away, as it was declared second-last
// a goes away, as it was declared third-last

All good, right? This doesn’t seem too hard to understand. However, you do need to be acutely aware of this rule, because there are times where, if you’re not concentrating, it’ll leave you utterly stumped. Here’s an example of what I’m talking about:

struct Firework {
    strength: i32
}

impl Drop for Firework {
    fn drop(&mut self) {
        println!("BOOM times {}!!!", self.strength);
    }
}
struct FireworkShow<'a> {
    start: Option<&'a Firework>,
    middle: Option<&'a Firework>,
    finale: Option<&'a Firework>
}
impl<'a> Drop for FireworkShow<'a> {
    fn drop(&mut self) {
        println!("Welcome ladies and gentlemen! Now, without further ado...");
        ::std::mem::drop(self.start);
        println!("Now, for our second item...");
        ::std::mem::drop(self.middle);
        println!("And now, for the grand finale...!");
        ::std::mem::drop(self.finale);
    }
}
fn main() {
    let mut show = FireworkShow { start: None, middle: None, finale: None };
    let firecracker = Firework { strength: 1 };
    let tnt = Firework { strength: 100 };
    show.start = Some(&firecracker);
    show.finale = Some(&tnt);
}

This is my attempt at creating a wonderful firework show in Rust. I’ve been a bit naughty and stolen the Rust Book’s firework set, but I’m sure they won’t mind. (To compensate, I’ve invited @steveklabnik over to watch the show.) My FireworkShow object contains all you need for a good show - a start, middle, and finale, and a Drop impl, so that when the show goes out of scope, it sets off all the fireworks by drop()ping them as well. I’m only taking a reference to the fireworks, using the &'a lifetime syntax to ensure the fireworks don’t explode before the show starts (that is, the fireworks last for as long as the show), whilst still ensuring people can gaze at the fireworks before the show starts (because we’ve only taken a &borrow).

But what’s this? The rustc-firework-safety-analysis-team won’t let me start the show!

fireworks.rs:29:24: 29:35 error: `firecracker` does not live long enough
fireworks.rs:29     show.start = Some(&firecracker);
                                       ^~~~~~~~~~~
fireworks.rs:25:11: 31:2 note: reference must be valid for the block at 25:10...
fireworks.rs:25 fn main() {
                          ^
fireworks.rs:27:48: 31:2 note: ...but borrowed value is only valid for the block suffix following statement 1 at 27:47
fireworks.rs:27     let firecracker = Firework { strength: 1 };
                                                               ^
fireworks.rs:30:25: 30:28 error: `tnt` does not live long enough
fireworks.rs:30     show.finale = Some(&tnt);
                                        ^~~
fireworks.rs:25:11: 31:2 note: reference must be valid for the block at 25:10...
fireworks.rs:25 fn main() {
                          ^
fireworks.rs:28:42: 31:2 note: ...but borrowed value is only valid for the block suffix following statement 2 at 28:41
fireworks.rs:28     let tnt = Firework { strength: 100 };
                                                         ^
error: aborting due to 2 previous errors

So, what’s going on here? Reading the compiler errors, it’s non-obvious to me what’s going on (okay, some of the more seasoned Rustaceans will spot the problem instantly, but let’s act as if I have no experience and I’m trusting rustc for help):

  • it seems to say that my fireworks don’t live long enough. That seems fair, let’s see the notes it’s given us:
  • ah, alright, the fireworks must be valid for the entire block. That makes sense, because we need them for our show.
  • But, hang on, they’re only living as long as the statement that declares them? That doesn’t make any sense!

It turns out that the compiler diagnostics aren’t really helpful here in fixing our problem. (That’s why I’m here, to tell you the solution after having to muddle through this myself.) The problem here is simple: remember, objects are deallocated in the reverse order from when they were declared. So this happens: (code snippet copied here for your reference)

fn main() {
    let mut show = FireworkShow { start: None, middle: None, finale: None };
    let firecracker = Firework { strength: 1 };
    let tnt = Firework { strength: 100 };
    show.start = Some(&firecracker);
    show.finale = Some(&tnt);
}
  • Alright, end of main(), everybody, let’s start deallocating stuff.
  • Okay, so looks like tnt was declared first, let’s kill that. (BOOM!)
  • Next up is firecracker, so let’s Drop that one too… (BOOM!)
  • And, finally, show - wait, just a darn minute, show still has a borrow of firecracker and tnt!!! Quick, unexplode them so show doesn’t try to drop random memory when we kill it!!!

Obviously, we can’t have show containing invalid references. We need to rewrite our function so that the show is only declared AFTER the fireworks are good and ready:

fn main() {
    let firecracker = Firework { strength: 1 };
    let tnt = Firework { strength: 100 };
    let mut show = FireworkShow { start: None, middle: None, finale: None };
    show.start = Some(&firecracker);
    show.finale = Some(&tnt);
}

Perfect! Our firework show is safe, and Rust has ensured that none of the fireworks will go off before we want them to.

Boxing the fireworks up for a firework convention

I’ve also started to think, however, that our simple Firework type is not enough - what if we want to add a more complex firework, like one of those ones which sits on the ground and spurts out sparks everywhere? Our code doesn’t account for this situation! (There are so many exploding things we’re missing out on!)

A particular example of a use-case which we’ll need this capability for is the new FireworkConvention. They’ve got all sorts of new types of fireworks there, and they also feature entire shows!

To solve this problem, then, I’m going to add a new trait, Explodable, for explodey things like fireworks, and we’ll store our fireworks as boxed trait objects (Box<Explodable>) that’ll let us store any kind of firework! I’ll put this together, along with the new FireworkConvention, in the below example.

struct FireworkConvention {
    small_stands: Vec<Box<Explodable>>,
    sponsored_exhibits: Vec<Box<Explodable>>,
    big_shows: Vec<Box<Explodable>>
}

trait Explodable: Drop { /* the ": Drop" means any `Explodable` object must
                            also have a `Drop` impl */
    /// How powerful the explosion is going to be.
    fn strength(&self) -> i32;
}

impl Explodable for Firework {
    fn strength(&self) -> i32 { self.strength }
}
impl<'a> Explodable for FireworkShow<'a> {
    fn strength(&self) -> i32 { 1000 } /* artificially inflated strength to get more people
                                          to watch our firework show */
}
fn main() {
    let firecracker = Firework { strength: 1 };
    let fireworkcorp_new_firework = Firework { strength: 50 }; /* ooh, interesting! */
    let tnt = Firework { strength: 100 };
    let mut show = FireworkShow { start: None, middle: None, finale: None };
    show.start = Some(&firecracker);
    show.finale = Some(&tnt);

    let mut fwc = FireworkConvention { small_stands: vec![], sponsored_exhibits: vec![], big_shows: vec![] };

    fwc.sponsored_exhibits.push(Box::new(fireworkcorp_new_firework));
    fwc.big_shows.push(Box::new(show));
}

Okay. We’ve made a new FireworkConvention object, which stores Vecs (dynamic arrays) of all the different types of exploding things one can find in such a convention - small stands of people with fireworks, sponsored exhibits from the likes of the famous FireworkCorp®, and big firework shows like ours. All of these things are nicely packed away in Boxes where the only thing they can do is tell the world how explodey they are (with strength()) and explode (via Drop). If you ask me, it’s a weird convention.

However, just as the convention is about to start, a bunch of Rust Firework Convention Safety officials walk in. They’ve got a few queries about our convention.

fireworks.rs:47:24: 47:35 error: `firecracker` does not live long enough
fireworks.rs:47     show.start = Some(&firecracker);
                                       ^~~~~~~~~~~
fireworks.rs:47:24: 47:35 note: reference must be valid for the static lifetime...
fireworks.rs:43:48: 53:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 43:47
fireworks.rs:43     let firecracker = Firework { strength: 1 };
                                                               ^
fireworks.rs:48:25: 48:28 error: `tnt` does not live long enough
fireworks.rs:48     show.finale = Some(&tnt);
                                        ^~~
fireworks.rs:48:25: 48:28 note: reference must be valid for the static lifetime...
fireworks.rs:45:42: 53:2 note: ...but borrowed value is only valid for the block suffix following statement 2 at 45:41
fireworks.rs:45     let tnt = Firework { strength: 100 };
                                                         ^
error: aborting due to 2 previous errors

However, this time, their demands seem to be different.

fireworks.rs:47:24: 47:35 note: reference must be valid for the static lifetime...

The static lifetime? Say what now? Oh, yeah, I sort of remember that thing. That’s the thing I told you about earlier in part III, where we needed to send stuff between threads. Let me quote myself:

The 'static lifetime is a special lifetime which means “lives for the entire program”.

Hang on. Just a darn minute. Is Rust saying that to attend the firework convention, items must ensure that they live forever? That doesn’t make that much sense, because then they can’t have any references inside them! Our show will never make it to the big convention.

Or will it? Just like the previous example, this is another time where rustc is a bit unhelpful. When it tells us that reference must be valid for the static lifetime, it’s actually because we told it that the FireworkConvention only accepts things with a static lifetime.

“But when? How?! I swear, officer, I never wrote any code with the word 'static in my life!”


Turns out that when we Boxed up our fireworks, Rust assumed that we meant 'static. By default, Rust assumes the content inside a Box lives for the static lifetime - that is, it doesn’t contain any references or anything like that. If you want something else, you need to tell Rust by adding + 'a inside the Box<> part, like so:

struct FireworkConvention<'a> {
    small_stands: Vec<Box<Explodable + 'a>>,
    sponsored_exhibits: Vec<Box<Explodable + 'a>>,
    big_shows: Vec<Box<Explodable + 'a>>
}

Once we do this, Rust realises that we actually wanted to allow our explodey things to be non-'static (after all, the FireworkCorp® doesn’t live forever - it’s bound to go bankrupt, what with Brexit recently and all that). Our code compiles, and the firework convention is allowed to continue. Hooray!

Conclusion (because my examples are massive and confusing)

So, if you remember nothing about the crazy firework show metaphors, the things you need to take away from this are the following:

  1. If you’re assembling a larger struct out of references to smaller structs, declare all the stuff being referenced before all the stuff you’re assembling it into.
  2. If you’re boxing up trait objects that don’t need to be 'static, tell the Rust compiler as it won’t do it for you.

Thanks for reading! If you have any comments, queries, or suggestions, either make a comment on Reddit /r/rust (thread here) or hit me up on Twitter @eeeeeta9. I’m going to try to stick to a more rigid schedule, so subscribe to my RSS feed for more Learn You a Rust!