UNPKG

@sputnick/lumiere

Version:

A styled component library used in my videos

126 lines (96 loc) 4.72 kB
# 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)