Rust Borrowing and References

🦀 Rust Borrowing and References

Borrowing and References are how Rust lets you use data without taking ownership.

This is what makes Rust safe, fast, and free from data races.

Think of it like this:

  • Ownership = you own the house

  • Borrowing = someone visits the house (can see or modify based on permission)


🧠 What Is a Reference?

A reference is like a pointer that:

  • Points to a value

  • Does not own it

  • Is guaranteed to be safe (no dangling pointers)

Syntax:

&value // immutable reference
&mut value // mutable reference

🔹 1. Immutable Borrowing (&)

You can borrow a value read-only.

fn print_length(s: &String) {
println!("{}", s.len());
}
fn main() {
let name = String::from(“Rust”);
print_length(&name);
println!(“{}”, name); // still valid
}

✔ Ownership stays with name
✔ Function only reads data
✔ Multiple immutable borrows allowed


🔹 2. Mutable Borrowing (&mut)

You can borrow a value with permission to modify it.

fn add_text(s: &mut String) {
s.push_str(" Language");
}
fn main() {
let mut text = String::from(“Rust”);
add_text(&mut text);
println!(“{}”, text);
}

✔ Value must be declared mut
✔ Only one mutable borrow at a time


🔑 Borrowing Rules (VERY IMPORTANT)

Rule 1: Multiple immutable borrows allowed

let s = String::from("Rust");

let r1 = &s;
let r2 = &s;

println!(“{} {}”, r1, r2);


Rule 2: Only one mutable borrow at a time

let mut s = String::from("Rust");

let r1 = &mut s;
// let r2 = &mut s; ❌ Error

r1.push_str(” Lang”);


Rule 3: Cannot mix mutable and immutable borrows

let mut s = String::from("Rust");

let r1 = &s;
// let r2 = &mut s; ❌ Error

println!(“{}”, r1);

👉 This prevents data races.


🔹 3. Borrowing and Scope

Borrowing follows scope rules.

let mut s = String::from("Hello");

{
let r = &mut s;
r.push_str(” Rust”);
} // r goes out of scope

let r2 = &mut s; // OK now


🔹 4. References Prevent Dangling Pointers

Rust will not compile code that creates dangling references.

fn dangle() -> &String {
let s = String::from("Rust");
&s // ❌ s is dropped here
}

✔ Rust catches this at compile time

Correct way:

fn no_dangle() -> String {
String::from("Rust")
}

🔹 5. Borrowing with Slices (&str)

Slices are references.

fn first_word(s: &str) -> &str {
&s[0..5]
}
fn main() {
let s = String::from(“Hello Rust”);
let word = first_word(&s);
println!(“{}”, word);
}

✔ Very efficient
✔ No ownership transfer


🔹 6. Borrowing in Functions (Best Practice)

Prefer references in function parameters.

fn display(name: &str) {
println!("{}", name);
}

✔ Works with String and &str
✔ No ownership issues
✔ Flexible & efficient


❌ Common Borrowing Mistakes

  • Multiple mutable references at once

  • Mixing & and &mut

  • Returning references to local variables

  • Forgetting scope ends borrowing


🧠 Borrowing Summary

Concept Meaning
&T Immutable reference
&mut T Mutable reference
Multiple & Allowed
Multiple &mut ❌ Not allowed
& + &mut ❌ Not allowed

You may also like...