<< Previous | Next >>

Daily Learnings: Wed, Dec 20, 2023

If you aren’t going all the way, why go at all? — Joe Namath

More Rust Learnings - Borrowing & References

I was able to get some more time during lunch today to continue my rust study from the very comprehensive freeCodeCamp YouTube course found here. I’m still only about 2.5hrs in, with 11.5hrs to go…

I found this topic really interesting as the Rust application that I’ve created uses references a lot without me realizing exactly what it was. Given that a good portion of the application was generated by Github Copilot / ChatGPT, I’m happy to better understand what the AI was actually suggesting.

Rules of References

  1. At any given time you can have either 1 mutable reference OR any number of immutable references
  2. References must always be valid, and cannot be dangling pointers to a piece of data that has already been dropped

Valid Borrowing Code Examples

fn main() {
    // Example of a valid, immutable reference
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);

    // Example of valid, mutable reference
    let mut s = String::from("hello");
    change(&mut s);

    // One last example
    let x: i32 = 5;
    let p: &i32 = &x;
    println!("The memory address of x is {:p}", p);
}

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

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

Invalid Code Examples - Violation of Rule 1

Example of violation of the first rule of borrowing: You can only have 1 mutable reference at any given time.

fn main() {
    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s;
    println!("{}, {}", r1, r2);
}

The fix for the above could be something like this:

fn main() {
    let mut s = String::from("hello");
    {
        let r1 = &mut s;
    }
    // At this point r1 is dropped and no longer active

    let r2 = &mut s;
    println!("{}", r2);
}

Example of violation of the first rule of borrowing: You can have EITHER 1 mutable reference or multiple immutable references to data, but not both at the same time.

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

    let r1 = &s; // No prob
    let r2 = &s; // No prob
    let r3 = &mut s; // PROBLEM!

    println!("{}, {}, and {}", r1, r2, r3);
}

This could be fixed like this:

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

    let r1 = &s;
    let r2 = &s;
    println!("{} and {}", r1, r2);

    // At this point variables r1 & r2 are dropped as they're no longer used in the program
    // So we can create a new, mutable reference
    let r3 = &mut s;
    println!("{}", r3);
}

Invalid Code Example - Violation of Rule 2

Example of violation of the second rule of borrowing: All references must be valid and not pointers to dropped data.

fn main() {
    let reference_to_nothing = dangle();
}

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

Instead, you might do something like this:

fn main() {
    let s: String = no_dangle();
}

fn no_dangle() -> String {
    let s = String::from("hello");
    s
}

References