Memory Safety in Rust
Rust’s ownership system eliminates entire classes of memory bugs while maintaining performance. Here’s how it works:
1. Ownership Rules
The foundation of Rust’s memory safety is built on three key ownership rules:
- Each value has exactly one owner
- When the owner goes out of scope, the value is dropped
- Assignment transfers ownership (moves)
Here’s a simple example demonstrating ownership:
fn main() {
let s1 = String::from("hello"); // s1 owns the string
let s2 = s1; // Ownership moves to s2
// println!("{}", s1); // Error! Value was moved
println!("{}", s2); // This works
}
2. Borrowing (References)
Borrowing allows you to reference data without taking ownership:
- Create read-only references with
&
- Multiple immutable borrows are allowed
- References must always be valid
fn calculate_length(s: &String) -> usize {
s.len()
}
fn main() {
let s = String::from("hello");
let len = calculate_length(&s);
println!("Length: {}", len);
}
3. Mutable References
When you need to modify borrowed data:
- Only one mutable reference is allowed at a time
- No immutable borrows can exist simultaneously
- Prevents data races at compile time
fn modify(s: &mut String) {
s.push_str(", world!");
}
fn main() {
let mut s = String::from("hello");
modify(&mut s);
println!("{}", s); // Prints "hello, world!"
}
Practical Example
Here’s a practical example that brings these concepts together:
fn process_text(text: &str) -> String {
let mut result = String::new();
result.push_str("Processed: ");
result.push_str(text);
result
}
fn main() {
let input = String::from("Rust is awesome");
let output = process_text(&input);
println!("{}", output);
}