DEV Community

Rishabh Gupta
Rishabh Gupta

Posted on

Lifetimes in Rust

Introduction

Rust is a system programming language which guarantees memory safety and prevents problems associated with it like data races between threads, memory leaks and dangling pointers by taking radically new approach to memory management. In this article I'd like to discuss about one such approach which they call Lifetimes.

Brief intro to borrowing and ownership

Borrowing, ownership and lifetimes, all three of them are concepts in Rust which complement each other and provide memory guarantees at runtime. To keep our focus on Lifetimes we'll briefly see what others are.

Borrowing

Example: let x = &y
Here x references y and is "almost" similar in functionality to C/C++ references.

Ownership

Rust doesn't have a garbage collector, so it follows a simple rule to clear out allocated memory.

fn foo() {
    let v = [1, 2, 3];
}
Enter fullscreen mode Exit fullscreen mode

When v goes out of scope it's memory will be cleared, even if it is dynamically allocated.

So what is Lifetime ?

In layman terms lifetime of a thing is "the length of time that thing is usable" and it means almost the same in Rust, the "thing" here being "variables".

Take a look at this code, and errors generated by compiler in the comments:

fn main() {
    let r;              
    {
        let i = 1;      
        r = &i;         // borrow occurs here
    }                   // `i` dropped here while still borrowed

    println!("{}", r);
}    // borrowed value needs to live until here

Enter fullscreen mode Exit fullscreen mode

What happens here is:

  1. We declare a variable r
  2. We go inside a scope 2.1. We initialize a variable i 2.2. Assign a reference of i to r
  3. Go out of scope
  4. Use r

At step 3, what happened was that memory for i was freed and r referred to a junk memory location.

The compiler gave us a sweet error message after step 4 saving us from a runtime disaster which C/C++ would have given us.

Okay, but why care ?

Let's see another example:

struct Car {
    model: &str
}
Enter fullscreen mode Exit fullscreen mode

You create a struct, and happily click that compile button, but...

error[E0106]: missing lifetime specifier

model: &str
        ^ expected lifetime parameter
Enter fullscreen mode Exit fullscreen mode

you get this message. You're scratching your head and thinking, "I didn't do anything wrong".

Well, by compiler logic the reference model will be destroyed as soon as the struct scope finishes. So how do you fix it:

struct Car<'a> {
    model: &'a str
}
Enter fullscreen mode Exit fullscreen mode

Notice the 'a, we have explicitly assigned a name to variable's lifetime, then in the statement model: &'a str we tell the compiler to keep the reference around for as long as we have the variable of type Car.

Point to take note is that, lifetimes are always there but most of the times they are implied automatically by the compiler so you don't have to explicitly assign them each time you borrow a reference.

Leveling up

We'll look at another code:

struct Person<'a> {
    name: &'a str,
    do_task: &'a Fn(&str)
}
Enter fullscreen mode Exit fullscreen mode

Here do_task is a reference to a function and it borrows a string reference, "but... we didn't define &'a str". &str means it is tied to the scope of function, hence the string reference will be cleared when the function completes.

While do_task and name are tied to the scope of variable which is of Person type.

Lifetimes is a new and interesting concept, but can be hard to understand the first time. Other resources you can look at are:

Happy coding!!

Discussion (1)

Collapse
ajinkyax profile image
Ajinkya Borade

You made it really easy to understand. I started in Rust today and was wondering what is this &'a