Borrowing is a mechanism that allows you to move around a pointer without claiming memory ownership. This ensures that there is no data race by enforcing strict rules to access a reference.
Why do you need Borrowing in Rust?
This feature allows you to temporarily access a reference without transferring ownership. It makes memory tracking easier for the compiler because there will be no memory leak caused by the borrower.
In more advanced use cases, such as concurrency, you cannot mutate a borrowed reference because the compiler already detects it. This feature avoids problems down the road.
The Rule
Rust has some strict rules to follow when you use the borrowing feature.
Here are the rules that you need to know:
One-to-Many Immutable Borrow or One Mutable Borrow
- Immutable Borrow
This valid Rust code has a
data
variable that is borrowed by x, y, and z. The borrower doesn’t mutate the data; hence, it is safe to borrow data
from multiple borrowers.fn main() { let mut data = vec![1,2,3]; let x = &data; let y = &data; let z = &data; println!("Borrowed data: {:?}, {:?}, {:?}", x, y, z); }
- Mutable Borrow
This valid Rust code borrow
data
variable, but it can mutate it. Because it is immutable, you cannot make multiple borrower.fn main() { let mut data = vec![1, 2, 3]; let x = &mut data; let y = &mut data; // invalid! <---------------------<< x.push(4); println!("Borrowed data: {:?}", x); }
Reference should always valid
You cannot mutate reference when it is borrowed
While you borrow a reference, you cannot change its original value. Because doing so can cause your borrowed value to be invalid.
fn main() { let mut x = 12; let y = &x; x += 1; // invalid! <---------------------<< println!("Original value: {:?} Borrowed value: {:?}", x, y); }
The Limitation
With all the rules that exist to borrow a reference, this is the limitation that can happen when you do so.
You cannot make immutable and mutable borrow at the same time
This is because rules 1 and 3 apply here. You don’t want the borrower to mutate a reference somewhere.
fn main() { let mut data = vec![1,2,3]; let x = &data; let y = &data; let z = &mut data; // invalid! <---------------------<< println!("Borrowed data: {:?}, {:?}, {:?}", x, y, z); }
Conclusion
The rules of borrowing in Rust exist for some reason. While it may be restrictive for your coding flexibility, you will find it useful later because it avoids many problems that bug other programming languages with manual memory management.
Happy borrowing. Just in Rust!