DEV Community

Subesh Yadav
Subesh Yadav

Posted on

🦀 Day 3 of #100DaysOfRust – Ownership, Borrowing & the Borrow Checker

Today, I went deep into one of Rust’s core ideas: Ownership. It’s a big reason Rust can guarantee memory safety without a garbage collector. I also learned how borrowing and the borrow checker work together with ownership to prevent bugs at compile time.

If you're coming from a JavaScript or TypeScript background like me, these ideas feel very different—but incredibly powerful.


🔐 What is Ownership?

Ownership in Rust is about managing memory safely and automatically. Instead of having a garbage collector or manual free() calls like in C, Rust uses ownership rules enforced by the compiler to decide:

  • Who owns a value
  • When it should be cleaned up
  • Who is allowed to use it

🧨 Safety = Absence of Undefined Behavior

Rust defines "safe" as not having undefined behavior. That means your code won’t crash randomly or behave unpredictably, especially around memory. Ownership helps Rust catch potential memory issues at compile time.


🎯 Ownership as a Discipline

  • Every piece of heap data must have one and only one owner.
  • When that owner goes out of scope, Rust automatically frees the memory.
  • This ensures no memory leaks, no double frees, no use-after-free bugs.

🗂️ Stack vs Heap

  • Stack: Stores fixed-size, known-at-compile-time data. Fast to access.
  • Heap: Stores dynamically sized data like String or Vec. Managed via ownership.
let x = 5;                      // stored on the stack
let s = String::from("hello");  // heap-allocated
Enter fullscreen mode Exit fullscreen mode

📦 Boxes and Collections

Heap allocation is done via pointers like Box, or in collections like Vec, String.

These types own the heap memory and clean it up when they go out of scope.


🚫 No Manual Memory Management

Rust does not allow you to manually free memory. This removes an entire class of bugs. You just manage ownership — Rust handles cleanup.


🧳 Move Semantics

When you assign a variable to another, the ownership is moved, not copied.

let s1 = String::from("hello");
let s2 = s1; // s1 is now invalid
Using s1 after this causes a compile error, because s1 no longer owns the data.
Enter fullscreen mode Exit fullscreen mode

📋 Cloning Avoids Moves

If you want to copy the data instead of moving it, use .clone():

let s1 = String::from("hello");
let s2 = s1.clone(); // s1 still valid
Enter fullscreen mode Exit fullscreen mode

🔄 Variables Can't Be Used After Being Moved

This rule protects against dangling references and memory bugs:

let s1 = String::from("hello");
let s2 = s1;
// println!("{}", s1); // ❌ Compile error: s1 was moved
Enter fullscreen mode Exit fullscreen mode

📎 References & Borrowing

A reference lets you access data without taking ownership.

&T is an immutable reference

&mut T is a mutable reference

fn print_length(s: &String) {
    println!("Length: {}", s.len());
}
Enter fullscreen mode Exit fullscreen mode

💡 References Are Non-Owning Pointers

They’re temporary and do not deallocate memory. You borrow the data, use it, and give it back.


🔍 Dereferencing Accesses the Data

Rust automatically dereferences when needed, but you can do it manually:

let x = 10;
let r = &x;
println!("Value: {}", *r); // dereference to get the value
Enter fullscreen mode Exit fullscreen mode

🚫 No Aliasing & Mutation Simultaneously

Rust prevents data races at compile time by enforcing this rule:

You can either have multiple immutable references
OR one mutable reference — never both.

let mut s = String::from("hi");
let r1 = &s;
let r2 = &s;
// let r3 = &mut s; // ❌ Compile error
Enter fullscreen mode Exit fullscreen mode

🔐 Borrowing Changes Permissions

Rust uses an RWO (Read / Write / Own) permission model internally:

  • Read (R): Read the data
  • Write (W): Mutate the data
  • Own (O): Move or drop the data

Borrowing temporarily transfers permissions.


🕵️ Borrow Checker Finds Violations

Rust’s borrow checker is a tool in the compiler that ensures:

  • No use-after-move
  • No dangling pointers
  • No simultaneous read/write to same data

🔁 Permissions Are Returned After Lifetime Ends

Once a reference goes out of scope, its permissions are returned to the original owner. You can then borrow again.


⌛ Data Must Outlive References

You cannot create a reference to data that goes out of scope.

let r;
{
    let x = 5;
    r = &x; // ❌ Error: `x` doesn't live long enough
}
Enter fullscreen mode Exit fullscreen mode

📝 Summary – What I Learned

✅ Ownership guarantees memory safety without GC
✅ Heap data is automatically deallocated when its owner goes out of scope
✅ Move semantics prevent double frees
✅ Borrowing allows references, but with strict rules
✅ The borrow checker is your friend — not your enemy 😄


🔗 Follow me on Twitter: @subeshyadav

💬 Let's chat if you're learning Rust too!

Top comments (0)