References in Rust are interesting. It is clear that there are two types of references: immutable and mutable.

1. Immutable

Immutable references allow reading a resource but the resource cannot be modified. There can be more than one immutable reference in scope for a given resource at the same time.

let hello = String::from("hello world");
let a = &hello;
let b = &hello; //valid second immutable reference because a is also immutable

fn get_string_length(s: &String) -> usize {
  s.len()
}

get_string_length(&a); //borrows ownership from hello

fn change_string(s: &mut String) {
  s.insert_str(0, "I say ");
}

// change_string(&b); compiler error: we provided an immutable reference

2. Mutable

Mutable references can modify their resource. There can be at most one mutable reference to a given resource in scope at the same time. Further, there cannot be a mutable borrow when there is also an existing immutable borrow in scope for the same resource.

let mut world = String::From("world");
let a = &mut world;
//let b = &mut world; //compiler error: an existing mutable reference already exists
//let c = &world;  //compiler error: cannot borrow immutable because we already borrowed as mutable

fn prepend_hello(s: &mut String) {
  s.insert_str(0, "Hello ");
}

prepend_hello(a);

ref

Immutable and mutable references use ‘&’ when we want to define them. Where then does the ref keyword fit in?

It wasn’t clear to me so I decided to dig into it a bit more. The first resource I found http://xion.io/post/code/rust-patterns-ref.html, detailed the differences by using pattern matching examples. When trying to match against a reference type, the & is important.

let foo = &42;
match foo {
  &42 => println!("Matched"),
  _ => println!("Not matched"),
}

The match statement here is matching against references, specifically against a reference to 42. On the other hand, ref is used when we want the match destructuring result to be a reference but we don’t necessarily want to match against a reference.

let foo = 42;
match foo {
  ref v => println!("v is a reference to {:?}", v), //v is a &integer
}

This is useful for avoiding issues where things are moved and ownership changes.

let v : Vec<(String, String)> = vec![String::from("first"), String::from("second")];

let (s, t) = v[0]; //compiler error: attempt to move out of index of v

println!("{}, {}", s, t);

In this example on the second line, the values from v[0] are moved into s and t respectively, which fails because copy is not implemented for String.

We can get around this by changing the second line to one of the following.

let (s,t) = &v[0];
let (ref s, ref t) = &v[0];

I think the second way is preferred but I don’t know if there is a practical difference between the two.