Skip to content

We are working on this site. Want to help? Open an issue or a pull request on GitHub.

Borrowing in Depth

Borrowing is Rust’s way of letting you have references to data without taking ownership of it. It’s similar to how JavaScript handles object references, but with strict rules enforced at compile time.

In Rust, there are two types of references:

let x = 5;
let y = &x;  // y is an immutable reference to x
println!("{}", *y);  // Dereference y to get the value
let mut x = 5;
let y = &mut x;  // y is a mutable reference to x
*y += 1;  // Modify x through y

Rust enforces three main rules for borrowing:

  1. You can have either one mutable reference or any number of immutable references
  2. References must always be valid
  3. No data races allowed
let mut data = vec![1, 2, 3];

// This would cause a data race in JavaScript
let first = &data[0];
data.push(4);  // Error: cannot borrow `data` as mutable because it is also borrowed as immutable

Rust’s borrow checker is smart enough to know when a reference is no longer used:

let mut x = 5;
let y = &x;  // First borrow
println!("{}", y);  // Last use of y
let z = &mut x;  // This is fine because y is no longer used

Functions can take references as parameters:

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

let s = String::from("hello");
let len = calculate_length(&s);

JavaScript’s approach to references is more relaxed:

const obj = { value: 5 };
const ref1 = obj;  // Both variables reference the same object
const ref2 = obj;  // Can have multiple references

ref1.value = 10;  // Modifies the shared object
console.log(ref2.value);  // 10

JavaScript developers often use the ref pattern in React:

function Component() {
  const inputRef = useRef(null);
  return <input ref={inputRef} />;
}

Rust’s iterators often use borrowing:

let v = vec![1, 2, 3];
for i in &v {  // Borrow v
    println!("{}", i);
}

The borrow checker is Rust’s compile-time mechanism that ensures memory safety.

Sometimes you need to mutate data through an immutable reference:

use std::cell::RefCell;

let data = RefCell::new(5);
*data.borrow_mut() += 1;  // Mutate through an immutable reference

You can split a borrow into multiple parts:

let mut v = vec![1, 2, 3];
let (first, rest) = v.split_at_mut(1);

Sometimes you need a struct that contains a reference to itself.

Borrowing is a powerful feature that allows Rust to provide memory safety without garbage collection.