2024-10-27
003 - Borrowing Rules in Real Functions
Borrowing Rules in Real Functions
Why this matters
Most borrow checker issues show up at function boundaries, not toy examples. This lesson focuses on patterns you can reuse in real code.
Learning goals
By the end, you should be able to:
- Design function signatures using
&Tand&mut Tappropriately. - Avoid conflicting borrows in the same scope.
- Return owned values or references intentionally.
Core rules refresher
- Many immutable borrows OR one mutable borrow at a time.
- References must never outlive the data they refer to.
Python baseline
def add_tag(tags, item):
tags.append(item)
items = ["rust"]
add_tag(items, "memory")
print(items)
Rust equivalent
fn add_tag(tags: &mut Vec<String>, item: &str) {
tags.push(item.to_string());
}
Runnable end-to-end example
#[derive(Debug)]
struct User {
name: String,
tags: Vec<String>,
}
fn add_tag(user: &mut User, tag: &str) {
user.tags.push(tag.to_string());
}
fn display_name(user: &User) {
println!("name: {}", user.name);
}
fn uppercase_name(user: &User) -> String {
user.name.to_uppercase()
}
fn main() {
let mut user = User {
name: "peter".to_string(),
tags: vec!["python".to_string()],
};
display_name(&user); // immutable borrow
add_tag(&mut user, "rust"); // mutable borrow
add_tag(&mut user, "memory");
let upper = uppercase_name(&user); // immutable borrow again
println!("upper = {}", upper);
println!("user = {:?}", user);
}
API design tip
Default to:
&Twhen reading,&mut Twhen mutating,Twhen taking ownership is intended.
Common pitfalls
- Keeping an immutable borrow alive while trying to mutably borrow.
- Returning references to local variables.
- Using
Stringinputs where&strwould be more flexible.
Quick practice
- Add
fn remove_tag(user: &mut User, tag: &str). - Change one function to take ownership and explain why.
Recap
Borrowing is mostly API design discipline. Clear function signatures make ownership flow obvious and code easier to maintain.
Next lesson: 004 - Lifetimes Without Fear.
Join the Journey Ahead!
If you're eager to continue this learning journey and stay updated with the latest insights, consider subscribing. By joining our mailing list, you'll receive notifications about new articles, tips, and resources to help you seamlessly pick up Rust by leveraging your Python skills.
Other articles in the series
- 000 - Memory Mastery Roadmap
- 001 - Memory Mastery: Zero-Cost Abstractions
- 002 - Stack vs Heap, Moves, and Clones
- 004 - Lifetimes Without Fear
- 005 - String, &str, and Allocation Patterns
- 006 - Smart Pointers (`Box`, `Rc`, `Arc`, `RefCell`)
- 007 - Interior Mutability and Tradeoffs
- 008 - Data Layout and Cache-Friendly Rust
- 009 - Concurrency Memory Safety (`Send`, `Sync`)
- 010 - Profiling and Benchmarking Rust vs Python