Login Register

What Are The Borrowing In Rust?

What Are The Borrowing?

Borrowing allows you to access a value without taking ownership of it. Instead of transferring ownership, Rust provides references (&) that allow you to “borrow” a value temporarily.

Borrowing is particularly useful when you want to read or modify a value without creating a copy or moving ownership.

Imagine your friend has a bike.

  • If you take ownership, the bike is yours, and your friend can’t use it anymore.
  • If you borrow the bike, you can ride it, but your friend still owns it. After you finish, they still have the bike.

That’s exactly what borrowing does in Rust. Instead of moving the data (ownership transfer), you just borrow a reference to the data and use it temporarily.

Why Borrowing Is Useful In Rust?

  • No unnecessary copies → It means you don’t need to clone data again and again.
  • Safe sharing → Multiple parts of the program can look at the same value without fighting over ownership.
  • Clear rules → Rust ensures you can’t accidentally change data in unsafe ways while others are still using it.

What Is References In Rust?

A reference in Rust is a pointer-like data type that allows you to access or modify the value of a variable without owning it. References are created using the & symbol.

A reference is like a pointer that allows you to see the value without taking it away. Borrowing means using that reference for a short time.

Types of Borrowing

There are two types of borrowing in Rust:

  1. Immutable Borrowing: You can access a value without modifying it.
  2. Mutable Borrowing: This allows you to access and modify a value easily.

Immutable Borrowing

Immutable borrowing allows you to access a value without changing it. You can create an immutable reference using the & symbol.

Example 1: Immutable Borrowing

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

// Immutable borrow
let reader = &book;
println!("Someone is reading: {}", reader);

// Mutable borrow
let editor = &mut book;
editor.push_str(" - Beginner Friendly");
println!("Updated book title: {}", editor);
}

Output:

Someone is reading: Rust Programming

Updated book title: Rust Programming - Beginner Friendly

In this code:

  • The function “calculate_length” borrows the string “s” as an immutable reference.
  • The original value “s” remains valid after the borrowing.

Rules of Immutable Borrowing

We can create more than one immutable reference (&value) at the same time.

Example 2: Multiple Immutable References

“Library Book Borrowing”

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

let reader1 = &book; // First reader borrows immutably
let reader2 = &book; // Second reader borrows immutably
let reader3 = &book; // Even a third one is fine!

// All of them are just reading, not editing
println!("Reader 1 is reading: {}", reader1);
println!("Reader 2 is reading: {}", reader2);
println!("Reader 3 is reading: {}", reader3);

// Not allowed:
// *reader1 = String::from("Trying to edit");
// This would throw an error because immutable borrowers can't change the book.
}

What Is Mutable Borrowing In Rust?

Mutable borrowing allows you to modify the borrowed value. We can create a mutable reference using the &mut symbol. But only one mutable borrower is allowed at a time (to avoid conflicts).

Example 3: Mutable Borrowing

Let’s create one simple project: “Editing a Grocery List”

fn main() {
let mut groceries = String::from("Milk, Bread");

println!("Before update: {}", groceries);

// Pass mutable reference to another function
add_items(&mut groceries);

println!("After update: {}", groceries);
}

// Function takes a mutable reference and updates the string
fn add_items(list: &mut String) {
list.push_str(", Eggs, Butter"); // Adding new items
}

Output:

Before update: Milk, Bread
After update: Milk, Bread, Eggs, Butter

Explanation of this code:

  • We had a grocery lists with “Milk, Bread”.
  • We borrowed it mutably in the add_items function.
  • Inside the function, we added more groceries.

Rules of Mutable Borrowing

  1. You can have only one mutable reference to a value at a time.
  2. You cannot mix mutable and immutable references to the same value.

Example 4: Mutable Borrowing Restrictions

We are Updating a To-Do List with Borrowing Restrictions:

fn main() {
let mut todo = String::from("1. Study Rust");

// First mutable borrow
let r1 = &mut todo;
r1.push_str(", 2. Drink Water");
println!("After first update: {}", r1);

// r1 is no longer used after this point,
// so we can safely create another mutable borrow.
let r2 = &mut todo;
r2.push_str(", 3. Go for a Walk");
println!("After second update: {}", r2);

// If we tried to mix immutable & mutable at the same time, it would be an error.
// let read_only = &todo; // Not allowed here if mutable borrow is active
// println!("Reading: {}", read_only);
}

Output:

After first update: 1. Study Rust, 2. Drink Water
After second update: 1. Study Rust, 2. Drink Water, 3. Go for a Walk

Explanation of this code:

  • First, r1 gets a mutable borrow and adds “2. Drink Water”.
  • After r1 is done, Rust allows us to create r2, another mutable borrow.
  • Then r2 adds “3. Go for a Walk”.
  • If we try to read a list (&todo) while writing (&mut, todo), Rust would stop us to avoid conflicts.

It means Rust enforces a rule, either multiple readers OR one writer at a time, never both.

How To Use Borrowing in Functions?

When you pass a value to a function, normally, the ownership might move into the function. This means the caller would no longer be able to use it.

Borrowing in Functions

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

// Function only "reads" using immutable borrow
print_length(&s);

// Function "modifies" using mutable borrow
append_world(&mut s);

println!("Modified string: {}", s);
}

fn print_length(s: &String) {
// Immutable borrow: we can read, but not change
println!("Length: {}", s.len());
}

fn append_world(s: &mut String) {
// Mutable borrow: we can change the value
s.push_str(", world!");
}

Output:

Length: 5
Modified string: Hello, world!

Borrowing and Scopes In Rust

Borrowing follows strict scoping rules. A reference becomes invalid as soon as it goes out of scope. This ensures memory safety. Once the scope ends (like when a { } block finishes), the reference disappears.

Borrowing and Scoping Example

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

{
let r = &mut s; // Mutable reference created
r.push_str(" is awesome!"); // Safe modification inside this block
} // r goes out of scope here (seat becomes free)

let r2 = &s; // Now we can borrow immutably
println!("{}", r2);
}

Output:

Rust is awesome!

Dangling References

A dangling reference happens when a reference (like a borrowed pointer) is pointing to memory that no longer exists.

Imagine writing down your friend’s hostel room number, but then your friend checks out of the hostel. Now your note is useless; it points to an empty room.

In many programming languages (like C/C++), this causes undefined behavior (program crashes, random values, or security issues). But in Rust, the compiler will stop you before such dangerous code can even run.

Dangling Reference Prevention

fn main() {
let r = dangling_reference();
println!("{}", r); // Compiler error
}

fn dangling_reference() -> &String {
let s = String::from("Rust"); // s is created here
&s // Trying to return a reference to s
} // s is dropped here → reference becomes invalid
  • Inside dangling_reference, a new String called s is created.
  • Then we try to return &s (a reference to s).
  • The problem is that as soon as the function ends, s goes out of scope.
  • Rust automatically frees the memory of s.

How To Use Borrowing in Loops?

Borrowing in loops is like showing items without giving them away. It allows you to check or use elements temporarily, but the original collection stays safe and reusable.

Example code:

fn main() {
let fruits = vec!["Apple", "Banana", "Mango"]; // A collection

println!("Fruits in the basket:");
for f in &fruits { // Borrowing each fruit instead of moving
println!("{}", f);
}

// Still can use fruits after loop, because we only borrowed them
println!("Total fruits: {}", fruits.len());
}

Output:

Fruits in the basket:
Apple
Banana
Mango
Total fruits: 3

Simple Exercise For Students

“Sharing Chocolates with Friends”

Imagine you have a box of chocolates.

  • You don’t want to give the whole box away (ownership).
  • Instead, you want to show how many chocolates you have for your friends (immutable borrow).
  • Later, you decide to add more chocolates to the same box (mutable borrow).

Code Example:

fn main() {
let mut chocolates = 5;

// Borrow immutably: just showing chocolates to friends
show_chocolates(&chocolates);

// Borrow mutably: add more chocolates to the box
add_chocolates(&mut chocolates);

// Final check
println!("Now you have {} chocolates in the box!", chocolates);
}

// Function only looks at the chocolates (does not change them)
fn show_chocolates(count: &i32) {
println!("You currently have {} chocolates.", count);
}

// Function changes the number of chocolates
fn add_chocolates(count: &mut i32) {
*count += 3; // add 3 more chocolates
println!("3 chocolates added!");
}

Output:

You currently have 5 chocolates.
3 chocolates added!
Now you have 8 chocolates in the box!

Learn Other Topics About Rust Programming