Rust notes - Ownership and borrowing
Hi there! welcome back, this time we will be back to the notes posts
revisiting some of the core concepts of Rust
. We will focus on the concepts of ownership and borrowing. These are developed in chapter 4 of the book, but we will also use other sources to internalize each one.
Ownership
Ownership is Rust’s most unique feature, and it enables Rust to make memory safety guarantees without needing a garbage collector.
In Rust memory is managed through a system of ownership with a set of rules that the compiler checks at compile time. None of the ownership features slow down your program while it’s running.
Ownership Rules
- Each value in Rust has a variable binding that’s called its owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped.
Let's see a couple of examples that introduce the next topic (semantics
)
This works as expected
fn main() {
let a = 1;
let b = a; // Copy semantics
println!("{},{}", a, b);
}
But this don't compile
fn main() {
let a = String::from("str example");
let b = a; // Move semantics
println!("{},{}", a, b);
}
Move and Copy semantics
- Move semantics
In the above examples, the second one don't compile. That is because Rust
by default have move
semantics, that means:
When assigning a variable binding to another variable binding or when passing it to a function(without referencing), the bound resources are moved to the new variable binding and we can not access the original variable binding anymore.
Also, the ownership
state of the original bindings is set to moved
state.
- Copy semantics
But, the other example compiles... That is because the type
we use implements the Copy
trait. ( has copy
semantic )
When assigning a variable binding to another variable binding or when passing it to a function(without referencing), the bound resources are made a copy and assign or pass it to the function.
Also, the ownership state of the original bindings is set to copied
state. Mostly primitive
types implement Copy
.
References and Borrowing
Remember the first rule of ownership
, a value
can have only one owner (a variable binding
). So, what happens when we need to pass the binding to another function or assign them to other variable bindings. In those cases, we are referencing the original binding, borrow the data of it.
Borrowing rules:
- Either have
multiple
immutable (&T) borrows - OR
exclusively
one mutable (&mut T) borrow
Looking resources for this post I found this post where it's recommended to use other naming convention that is more clear to me:
Shared references ( &T )
A shared reference
means that other references to the same value might exist, possibly on other threads (if T
implements Sync
) or the caller's stack frame on the current thread.
fn main() {
let a = vec![1, 2, 3];
let b = get_first_element(&a);
println!("{:?} {}", a, b); // [1, 2, 3] 1
}
fn get_first_element(a: &[i32]) -> i32 {
a[0]
}
Exclusive references ( &mut T)
An exclusive reference
means that no other reference to the same value could possibly exist at the same time.
fn main() {
let mut a = vec![1, 2, 3];
let b = change_and_get_first_element(&mut a);
println!("{:?} {}", a, b); // [4, 2, 3] 4
}
fn change_and_get_first_element(a: &mut [i32]) -> i32 {
a[0] = 4;
a[0]
}
** Examples based from learning rust
That’s all for today, we revisited the concepts of Ownership
and Borrowing
, in the next note
we will talking about Lifetimes
.
As always, I write this as a learning journal and any feedback is welcome.
Thanks!