Ownership keeps Rust memory-safe, but moving every value into every function would be inconvenient. Borrowing solves that problem.
A reference lets code access a value without becoming its owner. This is one of the most important Rust skills because well-designed Rust programs borrow data whenever ownership does not need to change.
What is a reference?
A reference refers to a value owned somewhere else. You create an immutable reference using &:
fn main() { let message = String::from("Hello Rust"); let len = calculate_length(&message); println!("{message} has {len} bytes"); } fn calculate_length(text: &String) -> usize { text.len() }calculate_length can read the String, but it does not own it. When the function returns, the original message remains available.
Owner message ──────────> String heap data │ └── &message ──> borrowed reference no ownership transferWhat is borrowing?
Creating a reference is called borrowing. Think of it as temporary access:
- The owner remains responsible for the value.
- The borrower can use the value according to the reference's permissions.
- The compiler ensures the reference cannot outlive the value it refers to.
Immutable references: &T
fn print_name(name: &String) { println!("Name: {name}"); } fn main() { let name = String::from("Melochip"); print_name(&name); print_name(&name); println!("Owner still has: {name}"); }Immutable references are read-only. Rust can allow multiple immutable references because none can modify the shared value.
Why can't an immutable reference modify data?
fn change(text: &String) { text.push_str(" updated"); }The compiler rejects this because &String does not provide mutable access.
Mutable references: &mut T
To modify a borrowed value, both the owner and the reference must permit mutation:
fn change(text: &mut String) { text.push_str(" updated"); } fn main() { let mut message = String::from("status"); change(&mut message); println!("{message}"); }Expected output:
status updatedThe core reference rules
At a given time, Rust's borrowing model enforces an important rule:
- You may have multiple immutable references to a value, or
- you may have a mutable reference that requires exclusive mutable access for the relevant borrow period.
This prevents data races and iterator/reference invalidation problems from appearing unchecked.
Multiple immutable references
let text = String::from("Rust"); let r1 = &text; let r2 = &text; println!("{r1} {r2}");This is safe because both references only read.
Conflicting mutable references
let mut text = String::from("Rust"); let r1 = &mut text; let r2 = &mut text; println!("{r1} {r2}");If the two mutable borrows overlap in use, the compiler rejects the code because concurrent mutation through multiple aliases could be unsafe.
Mutable and immutable borrows cannot conflict
let mut text = String::from("Rust"); let reader = &text; let writer = &mut text; println!("{reader} {writer}");If these borrows overlap, Rust prevents the mutable reference from coexisting with active immutable references.
Non-lexical lifetimes: borrow ends after its last use
Modern Rust often recognizes that a reference is no longer needed before the closing brace:
let mut text = String::from("Rust"); let r1 = &text; let r2 = &text; println!("{r1} {r2}"); // last use of immutable borrows let r3 = &mut text; r3.push_str(" language"); println!("{r3}");This works because the immutable borrows are no longer used when the mutable borrow begins.
Dereferencing with *
The * operator accesses the value behind a reference:
fn main() { let number = 10; let reference = &number; assert_eq!(10, number); assert_eq!(10, *reference); }For method calls and field access, Rust often performs convenient automatic referencing/dereferencing, so explicit * is not always necessary.
Borrowing is not copying
| Operation | Ownership | Typical syntax |
|---|---|---|
| Move | Transfers ownership | let b = a; for non-Copy owned values |
| Clone | Creates another owned value | a.clone() |
| Borrow | Owner stays the same | &a |
| Mutable borrow | Owner stays the same; temporary write access | &mut a |
Prefer &str over &String for read-only string parameters
When a function only needs to read text, a string slice is often more flexible:
fn greet(name: &str) { println!("Hello, {name}!"); } fn main() { let owned = String::from("Kaushik"); greet(&owned); greet("Melochip"); }We explore String, &str and slices in detail in Chapter 8.
Dangling references
A dangling reference would point to memory that has already been released. Safe Rust prevents this at compile time.
fn dangle() -> &String { let text = String::from("temporary"); &text }This cannot work: text is destroyed when the function ends, so returning a reference to it would be invalid.
Return the owned value instead:
fn no_dangle() -> String { String::from("safe owned value") }Practical example: update configuration
struct Config { environment: String, retries: u32, } fn print_config(config: &Config) { println!("env={}, retries={}", config.environment, config.retries); } fn increase_retries(config: &mut Config) { config.retries += 1; } fn main() { let mut config = Config { environment: String::from("dev"), retries: 3, }; print_config(&config); increase_retries(&mut config); print_config(&config); }The read-only function borrows with &Config; the update function borrows with &mut Config. Neither function takes ownership.
Common borrowing errors
Cannot borrow as mutable
let message = String::from("hello"); message.push_str(" world");Fix the owner:
let mut message = String::from("hello"); message.push_str(" world");Cannot borrow as mutable because it is also borrowed as immutable
Look for an immutable reference that is still used after you attempt the mutable borrow. Often the correct fix is to shorten the immutable borrow's usage rather than cloning the value.
Reference may outlive borrowed value
Make sure the owner lives long enough, or return/move ownership instead of trying to return a reference to a local temporary value.
Hands-on mini lab
cargo new borrowing_lab cd borrowing_lab- Write a function accepting &String and returning its length.
- Change it to accept &str .
- Create a function accepting &mut String that appends text.
- Try two simultaneous active mutable borrows and read the compiler message.
- Try returning a reference to a locally created String and understand why Rust rejects it.
cargo fmt cargo clippy cargo runBorrowing checklist
- Does the function need ownership? If not, borrow.
- Does it only read? Prefer an immutable reference.
- Does it modify? Use a mutable owner plus &mut .
- For text-only read parameters, consider &str .
- Do not reach for clone() automatically when the compiler reports a move problem.
Next chapter
Now that references make sense, continue with Chapter 8: Rust Strings and Slices to understand String, &str, UTF-8 and safe slicing.
Official reference
See the official Rust Book section References and Borrowing.
Join the discussion
0 CommentsAsk questions, add context, or share what worked.
More from Melochip Editorial
Learn what Playwright testing is, why modern QA teams use it, how testers can get started, and the major types...
7 min read · Sep 19, 2026
Learn how Kubernetes Deployments and ReplicaSets work with practical YAML, scaling, self-healing, rolling upda...
7 min read · Sep 17, 2026
Learn Kubernetes security best practices with RBAC, Secrets, ServiceAccounts, Pod securityContext, NetworkPoli...
6 min read · Sep 17, 2026Related articles
Learn Docker registries, image tags, Docker Hub, private registries, login, push, pull, naming rules, CI/CD us...
2 min read · Sep 17, 2026
Learn what Playwright testing is, why modern QA teams use it, how testers can get started, and the major types...
7 min read · Sep 19, 2026
Learn how Kubernetes Deployments and ReplicaSets work with practical YAML, scaling, self-healing, rolling upda...
7 min read · Sep 17, 2026
Learn Kubernetes security best practices with RBAC, Secrets, ServiceAccounts, Pod securityContext, NetworkPoli...
6 min read · Sep 17, 2026Stay in the loop. Get new Melochip articles
Receive Melochip article updates when new articles are available. Explicit opt-in only.
No comments yet. Start the discussion.