Rust Lifetime Annotations Explained
If you're like me, you may have walked away after reading about lifetime annotations understanding their purpose but not really getting why the developer needs to manually annotate them in many situations. I'll provide a quick example that clearly shows why the compiler really needs you to specify this info.
This article is not a comprehensive tutorial on lifetimes in Rust. I'll assume you have already gone over the basics of lifetimes and how to annotate them. Reading through this section in the official Rust book would be a good start.
First, let's take a look at some code that wouldn't even compile because lifetimes have not been specified. This is important because we're going to think through possible scenarios and see why the compiler can't deduce anything without some help from you. Remember that the Rust compiler does not look at the bodies of functions when compiling to determine correctness. It needs to have all the info it needs from the function signature (where lifetimes are specified). With that info out of the way, take a look at this code:
Should main compile? The answer is that the compiler doesn't know. It only looks at the function signature and, from what is provided, it doesn't know if the reference being returned from do_stuff
in line 17
is tied to n1
or n2
. If the result is tied to n1
, we would be okay to compile since n1
is in the same scope as result. But what if the result is tied to n2
? n2
goes out of scope once the inner scope is done on line 18
so we would then be trying to return a reference to some memory that has been freed. So now it becomes clear that the compiler needs you to specify more info so that it can determine the correctness of your code.
Let's now take a look at code where the lifetimes have been properly annotated:
This code will indeed a compile, and you have given the Rust compiler the information it needs to determine correctness. By giving the result being returned by do_stuff
a lifetime annotation of 'a
(which is the same lifetime as its first argument) and specifying a different lifetime for the second argument, you have given the compiler the information it needs to know that it is safe to compile. This info lets the compiler know even when n2
goes out of scope (line 17
) that it's okay because the lifetime of the result being returned from do_stuff
doesn't depend on the lifetime of n2
.