Lesson 7 of 20

References and Borrowing

References and Borrowing

Instead of transferring ownership, you can borrow a value by taking a reference to it.

Immutable References

&T is a reference to T. You can have as many immutable references as you like:

fn calculate_length(s: &String) -> usize {
    s.len()
} // s is a reference — nothing is dropped here

let s = String::from("hello");
let len = calculate_length(&s); // borrow s
println!("{} has {} letters", s, len); // s still valid

Mutable References

&mut T allows modifying the borrowed value, but with a restriction: you can have at most one mutable reference to a value in a given scope:

fn change(s: &mut String) {
    s.push_str(", world");
}
let mut s = String::from("hello");
change(&mut s);

The Borrowing Rules

  1. At any given time, you can have either one mutable reference or any number of immutable references — never both.
  2. References must always be valid (no dangling pointers).

These rules prevent data races at compile time — not at runtime.

Slices as References

&[T] is an immutable slice — a reference to a contiguous sequence of T:

fn first_element(slice: &[i32]) -> i32 {
    slice[0]
}

Your Task

Implement four functions using references (no ownership transfer):

  1. sum_slice(numbers: &[i32]) -> i32 — sum all elements.
  2. largest_in_slice(numbers: &[i32]) -> i32 — return the largest element.
  3. double_all(numbers: &mut Vec<i32>) — double every element in-place.
  4. count_positive(numbers: &[i32]) -> usize — count elements greater than zero.
Rust (Miri) loading...
Loading...
Click "Run" to execute your code.