🚀
3. Memory Management
Languages

Memory Management

Mar 202510 min read

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 drop function 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)
  • s1 is moved to s2 and s1 is 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);
}
  • clone method 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_string takes ownership of s and s is no longer valid
fn main() {
  let x = 5;
 
  makes_copy(x);
 
  println!("{}", x);
}
 
fn makes_copy(some_integer: i32) {
  println!("{}", some_integer);
}
  • some_integer is a copy of x and x is 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_back and then back to s2

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");
}
  • &mut is 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 time
fn 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 from

Rules 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 immutable
  • first_word returns a slice of s and s is borrowed immutably
  • s.clear tries to borrow s mutably 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[..]
}
 
  • s2 is a string literal of type &str and first_word accepts &str

Slices on Different Types

fn main() {
    let a = [1, 2, 3, 4, 5];
 
    let slice = &a[1..3];
 
    println!("{:?}", slice);
}

© 2026 Driptanil Datta. All rights reserved.

Software Developer & Engineer

Disclaimer:The content provided on this blog is for educational and informational purposes only. While I strive for accuracy, all information is provided "as is" without any warranties of completeness, reliability, or accuracy. Any action you take upon the information found on this website is strictly at your own risk.

Copyright & IP:Certain technical content, interview questions, and datasets are curated from external educational sources to provide a centralized learning resource. Respect for original authorship is maintained; no copyright infringement is intended. All trademarks, logos, and brand names are the property of their respective owners.

System Operational

Built with Love ❤️ | Last updated: Mar 16 2026