Skip to main content

🦀 Rust Borrowing

Table of Contents

Rust Borrowing
#

Borrowing allows you to have references to a value without taking ownership. Rust's borrowing rules ensure memory safety at compile time.

References and Borrowing
#

Use & to create a reference without taking ownership:

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

fn calculate_length(s: &String) -> usize {
    s.len()
} // s goes out of scope but doesn't drop what it refers to

Mutable References
#

Create mutable references with &mut:

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);  // Prints "hello, world"
}

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

Borrowing Rules
#

Rust enforces these rules at compile time:

  • You can have either one mutable reference OR any number of immutable references
  • References must always be valid
fn main() {
    let mut s = String::from("hello");
    
    // Only one mutable reference at a time
    let r1 = &mut s;
    // let r2 = &mut s;  // Error! Cannot borrow as mutable more than once
    
    println!("{}", r1);
}

Multiple Immutable References
#

You can have multiple immutable references simultaneously:

fn main() {
    let s = String::from("hello");
    
    let r1 = &s;  // OK
    let r2 = &s;  // OK
    // let r3 = &mut s;  // Error! Cannot have mutable ref while immutable refs exist
    
    println!("{} and {}", r1, r2);
}

Dangling References
#

Rust prevents dangling references at compile time:

fn main() {
    // let reference_to_nothing = dangle();  // Error!
}

// This won't compile
fn dangle() -> &String {
    let s = String::from("hello");
    &s  // Error: returns a reference to data owned by this function
} // s goes out of scope and is dropped

// Solution: return the String directly
fn no_dangle() -> String {
    let s = String::from("hello");
    s  // Ownership is moved out
}

Reference Scope
#

A reference's scope starts from where it is introduced and continues through the last time that reference is used:

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

    let r1 = &s;
    let r2 = &s;
    println!("{} and {}", r1, r2);
    // r1 and r2 are no longer used after this point

    let r3 = &mut s;  // OK: no other references in scope
    println!("{}", r3);
}

Related