Lab 4: Ownership, Borrowing, & Lifetimes

In today’s lab we will introduce the Rust programming language,
and the core ideas Rust uses to ensure memory safety without manual
memory management or garbage collection. These core ideas are
ownership, borrowing, and lifetimes.

Today’s lab will be accompanied by copious examples in person, so
be sure to show up on time and follow along as we work through them.

Ownership

Ownership is Rust’s core mechanism for ensuring memory safety.
The way that Rust works is that each piece of data has an owner,
and when this owner goes out of scope, Rust knows the data can be
reclaimed.

fnmain(){// This is how you get a pointer to 5 in Rust.// 5 is now allocated on the heap, and x is a// pointer to its memory location.letx=Box::new(5);// x goes out of scope here, and so the 5 on// the heap is automatically reclaimed,// without the programmer having to do anything.}

Owners can also give ownership to another identifier:

fnmain(){letx;{lety=Box::new(5);// this passes ownership to x.x=y;// y goes out of scope here, but ownership// has been transferred, and so the 5 on// the heap is not reclaimed yet.}// x goes out of scope here, and so the 5 on// the heap is reclaimed.}

This also works across function calls.

fnblah(x:Box<i32>){// ownership is passed into the function.// When the function ends, the 5 on the// heap will be reclaimed.}fnmain(){letx=Box::new(5);blah(x);}

By ensuring there is only one owner of any piece of data, Rust ensures it
can always correctly insert the memory reclamation.

Borrowing

Borrowing is what stops the ownership system from being a complete pain
to use. Not every function needs to own data it’s working with. Most
functions can instead work with a reference, via borrowing.

References in Rust are just pointers with two rules:

References are never null. (Rust actually doesn’t have null in the language)

There can either be (a) infinite immutable references or (b) exactly one mutable reference.

Here is an example of borrowing.

fnblah(x:&i32){// do something...}fnmain(){letx=5;blah(&x);}

And

fnmain(){letx=5;lety=&x;letz=&x;leta=&mutx;// Woah there!}

Also

fnmain(){letx=5;lety=&mutx;letz=&mutx;// can't have >1}

In the above code, blah gets a reference to x. Put another way, blah borrows x. The rules
for when memory reclamation happens are still the same, because Rust guarantees the owner always
outlives the borrows. How does Rust ensure this? Through lifetimes!

Lifetimes

Every identifier in Rust has an associated lifetime, which you can think of as a named scope.