My Lifetimes Project

58 points Keozon

I've been casually learning rust on and off for about 5 years now. Recently, in the last six months, I've completed some semi serious projects in it, but during all of those, I've been avoiding references in structs, keeping ownership simple (and potentially inefficient).

I thought I had at least a passable understanding of what lifetimes were, but recently started a project (warning, still very rough) using nom to parse markdown files for literate programming purposes. This particular project has lifetimes all over the place (although technically there's just one lifetime).

The particular application really cleared things up for me. Byte slices, and references to those bytes slices were the perfect teacher. A large file in memory, a contiguous sequence of bytes. Everything parsing this file pulls out views into some section of that slice. From idx A to idx B, I can now see the data there, between those points. I can cheaply pass around dozens, or even thousands, of these windows into the same data without copying a thing (except the indexes themselves), but if that original, contiguous chunk of bytes goes away, then all those windows are looking at the wrong thing.

So, the windows can't be around longer than the original slice of bytes. This helped me realize that, as well as really understand what the lifetime symbols I was writing in the code actually meant in a practical way. I had a few errors from the compiler that didn't tell me to add a lifetime, but I figured out that the compiler needed given a lifetime for a return value, to pair it to the input value.

I wish I had kept better notes of the little eureka moments along the way, but what I can remember is that I had a few misconceptions based on things I had misunderstood over the years:

  1. The word "lifetime" to me implies a start and end time. Lifetimes in rust really only deal with the end time. You can have the same lifetime refer to objects created at many times, as long as they end at the same time. Something along \expiration\\ would make more sense to me, in terms of nomenclature.

  2. Lifetimes aren't just there to make the developer able to refer to something concrete. E.g. they aren't just there to improve readability or error message output. They provide real consequences to the borrow checker, and changing lifetimes in a signature, while changing nothing else, can cause compilation to succeed or fail.

  3. Lifetime annotations are simply a way of tying inputs and outputs of a function together -- which part of the input affects the lifetime of the output. This ensures that returned values based on input values don't last longer than their owners. That no view into the giant chunk of contiguous data lives longer than that chunk of data itself.

So, while I'm sure that there will still be a few surprises for me along the way to complete understanding, I do feel like I fundamentally grasp lifetimes!

I want to thank everyone in the Rust community that has helped me recently, and along the long journey from complete noob to maybe -- just maybe -- a halfway decent rust developer, and a better developer in general.