
Languages
Memory Management
1. Garbage Collection:
- Automatically manages memory
- Available in Java, Python, etc.
- No control over memory allocation
- Larger program size
- Slows down the program
2. Manual Memory Management:
- Programmer manages memory
- smaller program size
- Available in C, C++, etc.
- Can lead to memory leaks and dangling pointers
- Error-prone
3. Ownership:
- Rust's way of managing memory
- Ensures memory safety without garbage collector
- No memory leaks or dangling pointers
- Control over memory allocation
- Faster than garbage collection
- Slower write time (fighting with the borrow checker)
Types of Memory
Stack
- fixed-size data stored on the stack
- size is known at compile time
- stack frames are pushed and popped (no need to free memory)
- very fast access
Heap
- dynamic-size data stored on the heap
- size is calculated at runtime and can grow or shrink
- slower access
Ownership Rules
1
Each value in Rust has a variable that is its owner
2
There can only be one owner at a time
3
When the owner goes out of scope, the value will be dropped
https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html (opens in a new tab)
Scope
{ // s is not valid here, it’s not yet declared
let s = String::from("hello"); // s is valid from this point forward
// do stuff with s
} // s goes out of scope, deallocates from memory- When a variable goes out of scope, Rust calls
dropfunction and cleans up the memory
Move
- Rust defaults to moving instead of copying
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
}Execution Output
Error: borrow of moved value: `s1`Execution Output
$ cargo run
35 | let s1 = String::from("hello");
| -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
36 | let s2 = s1;- Move (not shallow copy)
s1is moved tos2ands1is no longer valid
Copy
fn main() {
let x = 5;
let y = x; // x is copied
println!("x = {}, y = {}", x, y);
}- Rust has a copy trait for data types that are stored on the stack
Clone
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
}clonemethod that can be used to deep copy
Ownership and Functions
fn main() {
let s = String::from("hello");
takes_ownership(s);
println!("{}", s);
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
}Execution Output
Error: borrow of moved value: `s`some_stringtakes ownership ofsandsis no longer valid
fn main() {
let x = 5;
makes_copy(x);
println!("{}", x);
}
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}some_integeris a copy ofxandxis still valid as it is stored on the stack
fn main() {
let s1 = gives_ownership();
println!("{}", s1);
}
fn gives_ownership() -> String {
let some_string = String::from("hello");
some_string
}- ownership is transferred to
s1
fn main() {
let s1 = String::from("hello");
let s2 = takes_and_gives_back(s1);
println!("{}", s2);
}
fn takes_and_gives_back(a_string: String) -> String {
a_string
}- ownership is transferred to
takes_and_gives_backand then back tos2
References
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length)
}fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}&is a reference to the value- references don't have ownership of the underlying value
&is immutable by default
Mutable References
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}&mutis mutable reference, only one mutable reference is allowed at a time, in particular scope
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
}Execution Output
Error: cannot borrow `s` as mutable more than once at a timefn main() {
let mut s = String::from("hello");
let r1 = &s;
let r3 = &mut s;
println!("{}, {}", r1, r3);
}Execution Output
Error: cannot borrow `s` as mutable because it is also borrowed as immutable-
Mutable and immutable references cannot coexist
-
However, immutable references are allowed to coexist
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{}, {}", r1, r2);
let r3 = &mut s;
println!("{}", r3);
}- Immutable references go out of scope after
println! - Mutable reference is allowed after immutable references go out of scope
Dangling References
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}Execution Output
Error: missing lifetime specifier
this function's return type contains a borrowed value, but there is no value for it to be borrowed fromRules of References
1
At any given time, you can have either one mutable reference or any number of immutable references
2
References must always be valid
Slices
fn main() {
let s = String::from("hello");
let slice = &s[0..2];
println!("{}", slice);
}- Slices are references a contiguous sequence of elements in a collection, instead of referencing the whole collection
fn main() {
let s = String::from("hello world");
let word = first_word(&s); // 5
s.clear();
}
fn first_word(s: &String) -> int {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}fn main() {
let mut s = String::from("hello world");
let hello = &s[..5];
let world = &s[6..];
let full = &s[..];
let word = first_word(&s); // hello
s.clear();
println!("{}", word);
}
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i];
}
}
&s[..]
}Execution Output
Error: cannot borrow `s` as mutable because it is also borrowed as immutablefirst_wordreturns a slice ofsandsis borrowed immutablys.cleartries to borrowsmutably causing an error
fn main() {
let mut s = String::from("hello world");
let s2 = "hello world"; // string literal of type `&s`
let word = first_word(s2);
}
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[..i];
}
}
&s[..]
}
s2is a string literal of type&strandfirst_wordaccepts&str
Slices on Different Types
fn main() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
println!("{:?}", slice);
}