Ownership is is a core concept in Rust and it provides a way for Rust and its programmers to deal with memory management and reference validation. Each value in Rust has an Owner which is basically the scope that the value belongs to. This could be a function or block scope or a struct etc.
When you pass a value to a function (and in other scenarios that I will skip over for brevity)
the value is moved out of its current scope and becomes owned by the function.
It then belongs to the function until it ends, when the value will be dropped or deallocated.
This also happens when you shadow a variable that holds a value that isn't Copy
.
let my_string = String::from("rust is awesome.");
let my_other_string = my_string;
The value belonging to my_string
has now been moved to my_other_string
, since String
is not Copy
.
If you tried to use my_string
again, you'd get an error from Rust's Borrow Checker.
In cases where you don't want a function (or some other scope) to take ownership of a value, you can pass it to the function as a reference, or in Rust vocabulary, you can have the function borrow the value.
fn main() {
let my_string = String::from("why is the mascot a crab?");
borrow_and_print_question(&my_string);
println!("we can still use it in this scope since it was just borrowed! {}", my_string);
take_ownership_and_print_question(my_string);
println!("oh no. This will upset the borrow checker... {}", my_string);
}
fn borrow_and_print_question(some_string: &String) {
println!("Question: {}", some_string);
}
fn take_ownership_and_print_question(some_string: String) {
println!("My Question: {}", some_string);
}
Like we talked about before, if you try to use a value after it has been moved, the borrow checker will remind you that this is not something that Rust will allow.
Lifetimes are another principle of Rust's Ownership model which enables the Borrow Checker to guarantee that references will be valid when your program runs. This is helps to prevent dangling references and helps the Borrow Checker understand how different references relate to each other. A lifetime basically describes how long a given value will live, and therefore how long a reference to the value would be valid. A value's lifetime begins when it is first assigned and ends when it is last used. Ususally Rust's Borrow Checker automatically knows how long a reference to a value is valid but there are certain cases where you need to declare them explicitly, using generic lifetimes annotations.