Rust uses a borrow checker to enforce its ownership rules and ensure that programs are memory safe. The ownership rules dictate how Rust manages memory over the stack and heap.

As you write Rust programs, you’ll need to use variables without changing the ownership of the associated value. Rust provides a robust borrowing mechanism to encourage flexibility and code reuse.

What Is Borrowing in Rust?

Borrowing is accessing the value of a variable without taking ownership of the variable by referencing the owner. The borrow checker ensures that the reference is valid, and the data isn’t dropped using a construct called lifetimes.

A lifetime is how long a variable exists. Lifetimes start on variable creation and end on variable destruction. You can borrow the ownership of a variable, and when the borrowed reference is out of scope, ownership returns to the owner variable. Borrowing is a bit like the pointers you’ll find in languages like C++ and Go. But the Rust compiler uses the borrow checker to ensure programs are memory safe.

An Example of Borrowing in Rust

You can borrow the ownership of a variable by referencing the owner using the ampersand (&) symbol.

        fn main() {
    let x = String::from("hello"); // x owns "hello"
    let y = &x; // y references x, borrows "hello"
    println!("{}", x);
    println!("{}", y)
}

Without borrowing by referencing, the program would panic. It would violate the ownership rule that a value can have one owner, and two variables cannot point to the same memory location. Borrowing can be very useful in functions. Here’s an example of borrowing in a function, to keep ownership while calling other functions that take local variables as arguments.

        fn print_even(vectr: &Vec<i32>) {
    for values in vectr {
        if values % 2 == 0 {
            println!("{}", values);
        }
    }
}

The print_even function references a vector of 32-bit integers as its argument. It then prints lines of values that are multiples of two in the vector using a for-loop and the println! macro.

        fn main() {
    let number_vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    print_even(&number_vector); // ownership is borrowed, not moved

    println!("The main function retains ownership of the number vector{:?}", number_vector)
}

The main function declares the number_vector variable and assigns a vector of 32-bit integers to it. It then calls the print_even function and passes it a reference to the number_vector variable using the ampersand symbol.

The main function retains the ownership of the number_vector variable it can continue to use the value at its memory location.

result of borrowing by referencing in Rust

Borrowing and Mutating References

Functions can also modify borrowed variables using mutable references to them, before returning ownership.

However, unlike regular variables that can be set to mutable using the mut keyword, you must prefix mutable references with the ampersand symbol.

Before making mutable references, the variable that you wish to modify must be mutable.

        fn remove_value(vectr: &mut Vec<i32>) -> &Vec<i32> {
    vectr.remove(4);
    return vectr
}

The remove_value function takes in the reference of a mutable vector of 32-bit integers. It returns a vector of 32-bit integers after removing the value of the vector in the fourth index.

        fn main() {
    let mut nums = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    remove_value(&mut nums); // mutable reference here
    println!("{:?}", nums);
}

The function modifies the nums vector by calling remove_value and passing the mutable reference of a vector as an argument. On printing the vector, the prior fourth index of the vector doesn't exist.

Notice that the argument is a reference to a mutable vector.

result of Borrowing and Mutating References in Rust

It’s Important to Understand Ownership and Borrowing

You’ll need to understand ownership and borrowing to write efficient, memory-safe Rust code that compiles and runs. If your code doesn't follow the ownership rules, the borrow checker will detect it. You’ll need to make your program memory-safe for Rust to compile it.

The borrow checker is annoying when you're new to Rust. But, as you write more Rust code, you’ll get used to it and gain experience on writing memory-safe Rust code.