Skip to content

Memory Management

Updated
17 Sept 2026
4 min read
5 sections
Page 12 of 18
Rust

"@/components/blog/code-output";

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

Each value in Rust has a variable that is its owner
There can only be one owner at a time
When the owner goes out of scope, the value will be dropped

https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html

Scope

rust
{ // 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 drop function and cleans up the memory

Move

  • Rust defaults to moving instead of copying
rust
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;

    println!("{}, world!", s1);
}
Out · error
Error: borrow of moved value: `s1`
Out · 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;
4 more lines
 | -- value moved here
37 |
38 | println!("{}, world!", s1);
| ^^ value borrowed here after move
  • Move (not shallow copy)
  • s1 is moved to s2 and s1 is no longer valid

Copy

rust
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

rust
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();

    println!("s1 = {}, s2 = {}", s1, s2);
}
  • clone{:rust} method that can be used to deep copy

Ownership and Functions

rust
fn main() {
    let s = String::from("hello");

    takes_ownership(s);

    println!("{}", s);
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string);
}
Out · error
Error: borrow of moved value: `s`
  • some_string{:rust} takes ownership of s{:rust} and s{:rust} is no longer valid
rust
fn main() {
  let x = 5;

  makes_copy(x);

  println!("{}", x);
}

fn makes_copy(some_integer: i32) {
  println!("{}", some_integer);
}
  • some_integer{:rust} is a copy of x{:rust} and x{:rust} is still valid as it is stored on the stack
rust
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{:rust}
rust
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_back{:rust} and then back to s2{:rust}

References

rust
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)
}
rust
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()
}
  • &{:rust} is a reference to the value
  • references don't have ownership of the underlying value
  • &{:rust} is immutable by default

Mutable References

rust
fn main() {
    let mut s = String::from("hello");

    change(&mut s);

    println!("{}", s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}
  • &mut{:rust} is mutable reference, only one mutable reference is allowed at a time, in particular scope
rust
fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &mut s;

    println!("{}, {}", r1, r2);
}
Out · error
Error: cannot borrow `s` as mutable more than once at a time
rust
fn main() {
    let mut s = String::from("hello");

    let r1 = &s;
    let r3 = &mut s;

    println!("{}, {}", r1, r3);
}
Out · error
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
rust
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!{:rust}
  • Mutable reference is allowed after immutable references go out of scope

Dangling References

rust
fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello");

    &s
}
Out · error
Error: missing lifetime specifier

this function's return type contains a borrowed value, but there is no value for it to be borrowed from

Rules of References

At any given time, you can have either one mutable reference or any number of immutable references
References must always be valid

Slices

rust
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
rust
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()
}
rust
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[..]
}
Out · error
Error: cannot borrow `s` as mutable because it is also borrowed as immutable
  • first_word{:rust} returns a slice of s{:rust} and s{:rust} is borrowed immutably
  • s.clear{:rust} tries to borrow s{:rust} mutably causing an error
rust
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[..]
}
  • s2{:rust} is a string literal of type &str{:rust} and first_word{:rust} accepts &str{:rust}

Slices on Different Types

rust
fn main() {
    let a = [1, 2, 3, 4, 5];

    let slice = &a[1..3];

    println!("{:?}", slice);
}