Coming from Java the concept of ownership was alien - so to solidify this knowledge I'm going to attempt a brief summary in ~5 minutes.

Ownership

Each variable in Rust has an owner. Once ownership is declared, there is exactly one binding to any given resource*.

Once ownership is moved, the previous owner no longer has access.

let x = vec![1];
let y = x;

println!("{}", x[0])

//: # (error[E0382]: borrow of moved value: `x`)
let x = vec![1];
let y = x;

println!("{}", x[0])

//: # (error[E0382]: borrow of moved value: `x`)

Copies

Ownership rules do not apply to primitive types, or anything that implements the CopyCopy trait (which gives a type copy semantics instead of move semantics).

let a = 5 as i32;
let _y = a as i64;

println!("{}", a);

//: Finished [] target(s) in 0.65s
let a = 5 as i32;
let _y = a as i64;

println!("{}", a);

//: Finished [] target(s) in 0.65s

Functions

A function can also take ownership of a variable via its parameters.

fn new_owner(_x: Vec<i32>) {
  // my_vec is now owned by the new_owner function
}

let my_vec: Vec<i32> = vec![1]; // vector containing a single element, 1
new_owner(my_vec);

print!("{}", my_vec[0]);
// error[E0382]: borrow of moved value: `my_vec`
fn new_owner(_x: Vec<i32>) {
  // my_vec is now owned by the new_owner function
}

let my_vec: Vec<i32> = vec![1]; // vector containing a single element, 1
new_owner(my_vec);

print!("{}", my_vec[0]);
// error[E0382]: borrow of moved value: `my_vec`

Whenever you see f(value)f(value) you can know that f has received ownership of value.

Borrowing

Instead of taking ownership, a resource can be borrowed. Rather than passing by value, we can pass by reference. The reference borrows ownership of the variable, until the period where it is released again, at which point the prior owner can manipulate the data.

fn new_owner(_x: &Vec<i32>) {
  // my_vec is now borrowed by the new_owner function
  print!("{}", _x[0]);
}

let my_vec: Vec<i32> = vec![1]; // vector containing a single element, 1
new_owner(&my_vec);

print!("{}", my_vec[0]);
// Compiles and prints: 11
fn new_owner(_x: &Vec<i32>) {
  // my_vec is now borrowed by the new_owner function
  print!("{}", _x[0]);
}

let my_vec: Vec<i32> = vec![1]; // vector containing a single element, 1
new_owner(&my_vec);

print!("{}", my_vec[0]);
// Compiles and prints: 11

The references are immutable, so changes to internal state are forbidden.

You can take a reference (pointer) to an item, but you have to give it back. In particular, you have to give it back before the lifetime of the underlying item expires, as tracked by the compiler.

fn new_owner(_x: &Vec<i32>) {
  _x.insert(0, 1);
}
// error[E0596]: cannot borrow `*_x` as mutable, as it is behind a `&` reference
fn new_owner(_x: &Vec<i32>) {
  _x.insert(0, 1);
}
// error[E0596]: cannot borrow `*_x` as mutable, as it is behind a `&` reference

Mutable References

A mutable reference allows the mutation of borrowed resources.

fn new_owner(_x: &mut Vec<i32>) {
  _x.insert(0, 5);
  print("{}", _x[0]);
}
 
let mut my_vec: Vec<i32> = vec![1]; // vector containing a single element, 1
new_owner(&mut my_vec);
// Compiles and prints: 5
fn new_owner(_x: &mut Vec<i32>) {
  _x.insert(0, 5);
  print("{}", _x[0]);
}
 
let mut my_vec: Vec<i32> = vec![1]; // vector containing a single element, 1
new_owner(&mut my_vec);
// Compiles and prints: 5

Key Points

  • If you have a mutable reference to a value, you can have no other references to that value.
let mut s = String::from("Test");

let r1 = &mut s;
let r2 = &mut s;

println!("{}, {}", r1, r2);
error[E0499]: cannot borrow `s` as mutable more than once at a time
let mut s = String::from("Test");

let r1 = &mut s;
let r2 = &mut s;

println!("{}, {}", r1, r2);
error[E0499]: cannot borrow `s` as mutable more than once at a time
  • At any given time, you can have either one mutable reference or any number of immutable references.
let mut s = String::from("Test");

let r1 = &s;
let r2 = &mut s;

println!("{}, {}, and {}", r1, r2);
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
let mut s = String::from("Test");

let r1 = &s;
let r2 = &mut s;

println!("{}, {}, and {}", r1, r2);
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable

Lifetimes

Borrowing references to objects and then passing them around can be dangerous. If a resource that has been borrowed is deallocated by its owner we must ensure that other users can no longer access the value. In Rust this is done using lifetimes, which describe the scope of how long a particular reference is valid for.

Lifetimes are defined using the 'a syntax, which means 'the lifetime a'.

fn hello_world<'a>(x: &'a i32) { ... }
fn hello_world<'a>(x: &'a i32) { ... }

The functions seen before have implicit lifetimes provided through a process known as lifetime elision.

fn print(s: &str); // elided (implicit)

fn print<'a>(s: &'a str); // expanded (explicit)
fn print(s: &str); // elided (implicit)

fn print<'a>(s: &'a str); // expanded (explicit)

Structs

structstructs must define explicit lifetimes when containing a reference. This ensures that any reference to internal reference data within an object does not live longer than the owner.

struct Point {
  x: &'a i32,
  y: &'b i32,
}

fn main() {
  let tmp; 

  {
    let x: &i32 = &1;
    let y: &i32 = &2;
    let p1 = Point { x, y }; 
    tmp = &p1.x;
  } // p1 has exited its scope here
  
  println!("{}", tmp); // `p1.x` does not live long enough
}
struct Point {
  x: &'a i32,
  y: &'b i32,
}

fn main() {
  let tmp; 

  {
    let x: &i32 = &1;
    let y: &i32 = &2;
    let p1 = Point { x, y }; 
    tmp = &p1.x;
  } // p1 has exited its scope here
  
  println!("{}", tmp); // `p1.x` does not live long enough
}

Smart Pointers

Smart pointer types act similarly to standard pointers, but allow further fine-grained control over behaviour. When a developer uses memory that contains dynamically allocated data with smart pointers, they are automatically de-allocated.

Reference Cell

A reference cell is a smart pointer type that executes the borrowing rules at runtime rather than at compile time.

RefCell<T>RefCell<T> has the following rules:

  • If there is no reference to TT, you may get either a mutable or immutable reference.
  • If there is already a mutable reference to TT, you must wait until this reference is dropped.
  • If there are one or more immutable references to TT, you may get an immutable reference.
use std::cell::RefCell;
let container = RefCell::new(11);

{
    let _c = container.borrow();
    // You may borrow immutably multiple times
    assert!(container.try_borrow().is_ok());
    // Cannot borrow as mutable because it is already borrowed as immutable.
    assert!(container.try_borrow_mut().is_err());
}
use std::cell::RefCell;
let container = RefCell::new(11);

{
    let _c = container.borrow();
    // You may borrow immutably multiple times
    assert!(container.try_borrow().is_ok());
    // Cannot borrow as mutable because it is already borrowed as immutable.
    assert!(container.try_borrow_mut().is_err());
}

Reference Counting

Reference counts (Rc<T>Rc<T>) sum up the number of references made to an object, and where no references exist, the data can be de-allocated. A reference-counted pointer to an item allows shared ownership of the data, breaking one of the initial rules outlined earlier, and allowing data leakage within Rust, so must be used with care.

Graph data structures use Rc<T>Rc<T>, as multiple edges might point to the same node, and that node is conceptually owned by all the edges that point to it. a

use std::rc::Rc;
use std::cell::RefCell;

struct Node {
    next: RefCell<Option<Rc<Node>>>,
    val: i32
}

fn main() {
    let foo1 = Rc::new(Node { next: RefCell::new(None), val: 5 });
    let foo2 = Rc::new(Node { next: RefCell::new(Some(foo1.clone())), val: 10 });
    *foo1.next.borrow_mut() = Some(foo2.clone());
    // Node with value 10 <-> Node with value 5
}
use std::rc::Rc;
use std::cell::RefCell;

struct Node {
    next: RefCell<Option<Rc<Node>>>,
    val: i32
}

fn main() {
    let foo1 = Rc::new(Node { next: RefCell::new(None), val: 5 });
    let foo2 = Rc::new(Node { next: RefCell::new(Some(foo1.clone())), val: 10 });
    *foo1.next.borrow_mut() = Some(foo2.clone());
    // Node with value 10 <-> Node with value 5
}