Labs ICT
โญ Pro Login

References and Borrowing

Using data without taking ownership.

References and Borrowing

Imagine you've got a favorite book. You want your friend to read it, but you also want to keep it for yourself. That's exactly what references solve in Rust. Instead of giving away ownership, you just lend a reference โ€” a pointer to the data โ€” so both you and your friend can use it without anyone losing anything.

An immutable reference uses the & operator. You can have as many immutable references as you want because nobody is changing anything. It's like having five people read the same book โ€” no conflicts.

fn main() {
    let book = String::from("Rust Programming");

    // Multiple immutable references are fine
    let r1 = &book;
    let r2 = &book;

    println!("r1: {}", r1);
    println!("r2: {}", r2);
}
Try it Yourself ->

Mutable References

But what if your friend wants to jot down notes in the margin? Then you need a mutable reference with &mut. Here's the catch โ€” you can only have one mutable reference at a time, and you can't have any immutable references when a mutable one exists. This prevents data races at compile time.

fn main() {
    let mut book = String::from("Rust");

    // One mutable reference is allowed
    let r1 = &mut book;
    r1.push_str(" Programming");
    println!("r1: {}", r1);
}
Try it Yourself ->

The Borrowing Rules

Rust enforces two simple rules: at any given time, you can have either one mutable reference or any number of immutable references โ€” but never both. This might feel restrictive, but it eliminates entire classes of bugs that plague other languages. Data races, dangling pointers, null pointer exceptions โ€” Rust catches all of them before your code even runs.

fn main() {
    let mut data = vec![1, 2, 3];

    let r1 = &data;
    let r2 = &data;
    println!("{} {}", r1[0], r2[0]);

    // After r1 and r2 go out of scope, we can borrow mutably
    let r3 = &mut data;
    r3.push(4);
    println!("r3: {:?}", r3);
}
Try it Yourself ->

No Dangling References

In languages like C or C++, it's easy to accidentally create a pointer that points to memory that's been freed โ€” a dangling reference. Rust prevents this entirely. The compiler tracks lifetimes and ensures references never outlive the data they point to.

fn main() {
    let reference = no_dangle();
    println!("{}", reference);
}

fn no_dangle() -> &String {
    let s = String::from("hello");
    &s  // This works because s is still alive
}

If you tried to return the owned value and a reference to it, Rust would catch the error. References are always safe because the compiler has your back.

Try it Yourself ->

๐Ÿงช Quick Quiz

What symbol creates an immutable reference?