References

Let's take a look at some code that includes a function that takes in a string and returns the length, then, after that function, prints out both the contents of the string and the length:

main.rs
fn main() {
    let s1 = String::from("hello");

    let (s1, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: String) -> (String, usize) { // s is a String
    (s.clone(), s.len())
} // Here, s goes out of scope and `drop` is called

That is... sorta gnarly. We could potentially copy s1 into calculate_length instead of returning a tuple, but either way... not great! Luckily, Rust does not actually require us to do this. With Rust, you can pass a value as a reference, which enables the function or variable to get the contents without taking ownership. You do this by putting an ampersand (&) in front of both the type and the variable when it's passed, like so:

main.rs
fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize { // s is a reference to a String
    s.len()
} // Here, s goes out of scope. But because it does not have ownership
  // of what it refers to, nothing happens.

The two big changes here are calculate_length(&s1) and calculate_length(s: &String). By default, if something is a reference, it is immutable. You can make a reference mutable by using &mut instead of &. Here's an example:

main.rs
fn main() {
    let mut s = String::from("hello");

    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

Knowing this, let's go ahead and redo our code for the team scores (used previously here and here):

main.rs
use std::collections::HashMap;

fn main() {

  let mut scores = HashMap::new();

  scores.insert(String::from("Blue"), 10);
  scores.insert(String::from("Yellow"), 50);

  get_score(&scores, String::from("Red"));
  get_score(&scores, String::from("Blue"));
}

fn get_score(scores: &HashMap<String, i64>, team_name: String) {
  let score = match scores.get(&team_name) {
                Some(num) => num.to_string(),
                None => "nonexistent".to_string()
              };
  println!("{}'s score is {}", team_name, score);
}

In summary, here are the rules for references:

  1. At any given time, you can have either but not both of:

    1. One mutable references

    2. Any number of immutable references

  2. References must always be valid

Last updated