CS3984 Computer Systems in Rust



References

  • References are pointers that must always be initialized.
  • Taking a reference is known as borrowing.
fn main() {
    let x1: Box<i32> = Box::new(5);
    let r1: &Box<i32> = &x1;
}


  • The reference r1 borrows x1.

References (2)

  • Dereferencing a pointer accesses its data.
  • Box is a smart pointer, which acts like a pointer but have additional metadata and capabilities.
fn main() {
    let x1: Box<i32> = Box::new(5);
    let n1: i32 = *x1;

    let r1: &Box<i32> = &x1;
    let n2: i32 = **r1;

    let r2: &i32 = &*x1;
}


References (3)

References do not own the value they point to.

fn main() {
    let s = String::from("crab");
    display(&s);
    println!("(2) {}", s);
} // `s` is freed here

fn display(name: &String) {
    println!("(1) {name}");
}


References (4)

References can be mutable. This requires the referent to also be mutable.

fn main() {
    let mut s1 = "prawn";

    let r1 = &mut s1;
    *r1 = "crab";

    println!("{s1}");
}


This prints crab.

Mutable Mutable References

Note the difference between a mutable reference and a mutable variable:

fn main() {
    let mut s1 = "prawn";
    let mut s2 = "words";

    let mut r1 = &mut s1;
    *r1 = "crab";

    r1 = &mut s2;
    *r1 = "language";

    println!("{s1} {s2}");
}


This prints crab language.

Mutable References and Functions

fn main() {
    let mut x1 = 5;
    increase(&mut x1);
    println!("{x1}");
}

fn increase(r: &mut i32) {
    *r += 1;
}


This prints 6.

References and Aliasing

At any point in time, you can have either:

  • One mutable reference
  • Any number of immutable references
fn main() {
    let x1 = 5;
    {
        let r1 = &mut x1;
    }
    {
        let r1 = &x1;
        let r2 = &x1;
        let r3 = &x1;
        let r4 = &x1;
    }
}


References and Aliasing (2)

Variables cannot be mutated while they are borrowed.

fn main() {
    let mut x1 = 5;

    // `x1` is borrowed here
    let r1 = &x1;

    // `x1` is mutated here
    x1 = 100;

    // The borrow of `x1` is later used here
    println!("{} {}", x1, *r1);
}


References and Aliasing (3)

Variables cannot be mutated while they are borrowed.

fn main() {
    let mut x1 = 5;

    // `x1` is borrowed here
    let r1 = &x1;

    // `x1` is mutated here
    x1 = 100;

    println!("{}", x1);
}


This compiles because the Rust compiler determined that r1 (and by extension, the borrow of x1) is not used after the line x1 = 100.

Dangling References

References must always be valid.

fn main() {
    let animal: &String = get_crab();
    println!("{}", animal);
}

fn get_crab() -> &String {
    let s = String::from("crab");
    &s
}


  1. The variable s owns the string "crab".
  2. The variable s goes out of scope when get_crab returns.
  3. The memory for "crab" is deallocated when get_crab returns.
  4. If compiled, the variable animal will point to deallocated memory.