@sputnick/lumiere
Version:
A styled component library used in my videos
126 lines (96 loc) • 4.72 kB
Markdown
# Ownership and borrowing
To understand how rust memory management differs from other languages, we must
understand two data structures: the stack and the heap. In computer science, the
stack and heap are two regions of memory that are used for storing data during
program execution.
## The Stack
The stack is a region of memory that is used for storing variables who's size is
known at compile time such as intagers.
When functions are called, their local variables get pushed onto the top of the
stack, and when a function returns, they are popped off
Because of how ordered the stack is, memory can be allocated and de-allocated
quickly.
But what if we dont know a variable's size is unknown at compile time? or what
if its size changes during execution? such variables must be stored on the heap.
## The Heap
Unlike a stack, a heap is a free-form data structure, which means that variables
can be added or removed from the heap in any order. When a program needs to
allocate memory for a variable has a dynamic size, or a variable that has a
longer lifetime than the stack, it requests a chunk of memory from the heap in
which it can store its memory. Since the next chunk of memory's position is not
known, the operating system needs to find a chunk of memory that is large enough
to contain our variable - which as you can imagine takes more time compared to a
stack which already knows where the top of the stack is.
## Ownership & Referencing
In Rust, every value has an owner, and there can only be one owner at a time.
When the owner goes out of scope, the value is dropped, and the memory it
occupies is freed. Consider this example:
```rs
fn main() {
let s = String::from("Rust");
}
```
Here, we create a new string `s` and allocate memory for it on the heap. When
`s` goes out of scope on line 3, it will be freed automatically.
Now lets move onto borrowing. In rust, we can borrow values instead of taking
ownership of them. Borrowing allows multiple parts of code to access the same
value without copying it. There are two types of borrows: immutable borrows and
mutable borrows.
### immutable borrows
immutable borrows are created by prefixing a variable with the `&` symbol, and
they allow read-only access to the value. An infinate number of immutable
references can be made, so long as there is no mutable reference. Here is an
example of how a mutable reference can be used:
```rs
fn main() {
let s = String::from("Rust");
let len = calculate_length(&s);
}
fn calculate_length(s1: &String) -> usize {
s1.len()
}
```
Here, we pass a reference to the string `s` to the `calculate_length` function
using `&s`. The function takes an immutable borrow of `s` using `&String` and it
returns its length. Note that we cannot modify the value of `s` inside the
function because we lack mutability. On the right is how this looks like in
memory. You can see `s` points to a `&Str` type which points to the point in the
heap where the characters are stored. `s1` is just a reference to `s` as such:
Mutable borrows, on the other hand, are created using the `&mut` keyword, and
they allow for read-write access from inside the function. Only one mutable
reference can exist at any given time, and if one does, there must be no
immutable reference to the variable. Lets see an example:
```rs
fn main() {
let mut s = String::from("Rust");
change_string(&mut s);
}
fn change_string(s1: &mut String) {
s1.push_str("y!");
}
```
Here, we pass a mutable reference of the string `s` to the `change_string`
function using `&mut s`. The function appends a new string to `s` to change `s`
from `Rust` to `Rusty!`. Once the end of the function is reached, the reference
(s1) goes out of scope and so we are free to create a new mutable reference to
s.
## woah that was alot
lets recap, you can either have 1 mutable reference OR as many immutable
references as you want.
Finally, lets look at the `&str` type seen before. The `&str` type is a borrowed
string slice that points to a selection of a string. Unlike the String type,
`&str` is immutable and cannot be modified because it does not start with
`&mut str`. Lets see an example of it in action:
```rs
fn main() {
let s = String::from("Rust");
let slice = &s[0..2];
println!("First two characters: {}", slice);
}
```
Here, we create a new string slice named `slice` that borrows the first two
characters of `s` using the `&` symbol and the range operator `..`. We can then
print the string slice using the `println!` macro.
For more information, i highly recommend the rust book to learn more about
referencing and borrowing in rust (or subscribe to catch the next video on
de-referencing in rust)