Rust Ownership, Borrowing, and Lifetimes Mark McDonnell
Rust Ownership, Borrowing, and Lifetimes Mark McDonnell
Rust Ownership,
Borrowing, and Lifetimes
2021-03-28 Mark McDonnell 14 mins
read
1. Ownership
2. Borrowing
3. Lifetimes
Stack Memory
The stack is memory that is available to your
program at runtime. Data that is assigned to
variables or passed as arguments to function
calls are allocated onto the stack in a ‘Last In,
First Out’ (LIFO) model, much like a stack of
plates.
Heap Memory
The heap is memory that is also available to
your program at runtime but is much slower
to access than stack memory. This is because
it requires a ‘pointer’ to locate the stored
data, of which the storage area is large and
very unorganized.
Ownership
The rules for ownership are quite simple:
fn main() {
let a = 123;
let b = a;
fn main() {
let a = String::from("foo");
let b = a;
fn main() {
let a = String::from("foo");
let b = a.clone();
Borrowing
The concept of borrowing is designed to
make dealing with ownership changes easier.
It does this by avoiding the moving of
owners. The way it does this is by letting
your program provide a ‘reference’ to the
data. This means the receiver of the reference
(e.g. a function, struct field or a variable etc)
can use the value temporarily without taking
ownership of it.
fn main() {
let s = String::from("foo");
fn main() {
let mut s = String::from("foo");
borrow(&mut s);
take_ownership(s);
fn take_ownership(s: String) {
println!("s: {}", s); // s: foobar
}
fn main() {
let mut s = String::from("foo");
borrow_no_mut(&s);
borrow_with_mut(&mut s);
fn borrow_no_mut(s: &String) {
println!("s: {}", s) // foo
}
Gotchas
You can’t take a reference and then
modify the original variable’s value.
fn main() {
let mut x;
x = 42;
let y = &x;
x = 43;
println!("{:?}", y);
}
$ cargo run
Compiling chapter1 v0.1.0 (/Users/integralist/
warning: value assigned to `x` is never read
--> src/main.rs:5:5
|
5 | x = 43;
| ^
|
= note: `#[warn(unused_assignments)]` on by def
= help: maybe it is overwritten before being re
fn main() {
let mut x;
x = 42;
{
let y = &x;
println!("{:?}", y); // 42
}
x = 43;
println!("{:?}", x); // 43
}
Another gotcha:
fn main() {
let mut s = String::from("foo");
let a = &mut s;
borrow(a);
let b = &mut s;
borrow(b);
fn main() {
let mut s = String::from("foo");
{
let a = &mut s;
borrow(a);
println!("a: {}", a); // foobar
} // <-- a is dropped
let b = &mut s;
borrow(b);
println!("b: {}", b); // foobarbar
}
Another gotcha:
fn main() {
let mut s = String::from("foo");
borrow(b);
fn main() {
let mut s = String::from("foo");
{
let a = &s;
println!("a: {}", a); // foo
} // <-- immutable reference `a` is dropped
let b = &mut s;
borrow(b);
println!("b: {}", b); // foobar
}
fn main() {
let r = return_ref();
println!("r: {}", r); // foo
}
fn main() {
let r = return_ref();
println!("r: {}", r); // foo
}
Lifetimes
Lifetimes are tightly coupled to ‘references’.