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)