Rust Ownership and Borrowing
Understanding Rust's Ownership Model
Rust's ownership system is a collection of memory management rules that are checked and prevented at a compiler-time level.
The purpose of this is to highlight the potential to avoid memory leaks and to manage threads without the use of garbage collector.
The vital ideas that lie at the heart of our language are ownership, borrowing, and lifetimes.
Ownership Rules and Borrowing Rules
Ownership Rules:
- Different Rust’s values have certainly a single owner.
- When an owner leaves a scope, the value becomes inaccessible.
- A value simply cannot possess more than one owner.
Example:
fn main() {
let s1 = String::from("hello"); // s1 owns the string
let s2 = s1; // Ownership of the string is moved to s2
// println!("{}", s1); // This line would cause an error because s1 no longer owns the string
println!("{}", s2); // s2 owns the string
}
Borrowing Rules:
- You can take either immutable or mutable value, but you cannot take them at the same time.
- Each immutable incarnation can have several references, while only one mutable reference is allowed.
- References have to be valid.
Example:
fn main() {
let s1 = String::from("hello");
// Immutable borrowing
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
// Mutable borrowing
let mut s2 = String::from("hello");
change(&mut s2);
println!("s2 is now '{}'", s2);
}
fn calculate_length(s: &String) -> usize {
s.len() // Immutable borrow
}
fn change(s: &mut String) {
s.push_str(", world"); // Mutable borrow
}
Lifetimes and References
Lifetimes:
Lifetimes are utilised to express the duration of time references should remain valid.
These prevent dangling references which occur when a reference is pointing to the data that doesn’t exists anymore.
Example:
fn main() {
let r;
{
let x = 5;
r = &x; // Error: `x` does not live long enough
}
println!("r: {}", r);
}
The above code nowhere contains the life for x. This impair would be overcome by Rust forcing one to annotate lifetimes when necessary.
Lifetime Annotations:
Lifetime annotations tell Rust how the lifetimes of references relate to each other.
Example:
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
In this case, <a !>: it - a lifetime annotation. The lifetime of the returned reference will also have that same span of time, just like the x and y refernces.
Summary
- Ownership: Manages memory by ensuring each value has one owner, which controls the value's lifecycle.
- Borrowing: Allows references to a value without taking ownership, adhering to strict rules to ensure safety.
- Lifetimes: Save the dead links in a database format to access upon requirement and avoid dangling references.
Rust's system of ownership, borrowing, and lifetime works in unison to offer memory safety settings without the need of a collector garbage collection which guarantees for efficient and safe memory management.