rust-ownership-reglas

Ownership in Rust: rules for managing memory

  • 5 min

The Ownership is the system that assigns an owner to each value and determines when it should be destroyed.

Welcome to ownership. It is one of the most important concepts in Rust and allows managing resources without needing a garbage collector.

In most languages, you don’t think about “whose data is this”.

  • In Python: “I create a list and the garbage collector will delete it when no one is looking.”
  • In C: “I request memory, and I have to remember to free it myself or it explodes.”

In Rust, each value has an “owner” and the compiler controls when that ownership changes and when the value should be destroyed.

Scope

Before looking at the rules, we need to understand the concept of Scope. Scope is basically the range within the code where a variable is valid.

In Rust, scope is usually delimited by curly braces {}.

fn main() { // Start of main's scope

    { // Start of an inner scope
        let s = "hello"; // 's' is born here and is valid
        println!("{}", s);
    } // End of inner scope. 's' is no longer valid here.

    // println!("{}", s); // ❌ Error! 's' no longer exists.
}
Copied!

This seems obvious with simple Stack variables (as we saw in the previous article). But the beauty of Rust is that it applies this scope logic to manage Heap memory.

The rules of ownership

The compiler verifies that the code follows these three rules:

  1. Each value in Rust has an owner.
  2. There can only be one owner at a time.
  3. When the owner goes out of scope, the value will be dropped.

Let’s break them down.

One owner and automatic cleanup

Imagine we use a String type, which manages a dynamically sized buffer on the Heap.

fn main() {
    {
        let s = String::from("text on the heap"); // s is the OWNER
        // Memory has been requested on the Heap.
    }
    // Here the curly brace closes.
    // s goes out of scope.
    // Rust automatically calls 'drop'. The memory is freed.
}
Copied!

You didn’t have to write free or delete. There is also no garbage collector watching. Simply, Rust inserts a call to a special function called drop right where the owner’s scope ends.

This is known in C++ as RAII (Resource Acquisition Is Initialization), but in Rust it is mandatory and the compiler enforces it.

Only one owner at a time

At this point, our mind usually goes “click”.

In other languages, you can have multiple variables pointing to the same object in memory without issues.

// JavaScript
let a = [1, 2, 3];
let b = a; // Both point to the same array
// If I modify 'b', 'a' also changes.
Copied!

In Rust, this would violate the “single owner” rule. Why? Because if a and b both pointed to the same location on the Heap, when both went out of scope, both would try to free the same memory. That is a severe error called Double Free, which corrupts memory and creates security holes.

Therefore, in Rust, this happens:

let s1 = String::from("hello");
let s2 = s1; // OWNERSHIP TRANSFER!

println!("{}", s1); // ❌ Error: value borrowed here after move
Copied!

When we do let s2 = s1, Rust says: “Okay, s2 is the new boss. s1, you’re fired, you’re worthless now.”

Rust invalidates the first variable. You can no longer use s1. This guarantees that, when the function ends, only s2 will try to free the memory. Security problem solved.

String and Copy types

You might be wondering: “Wait, in the variables article we did let x = 5; let y = x; and we could use both. Did you lie to me?”

No. The real difference is that some types implement the Copy trait, while others do not.

Integers, floats, booleans, and characters implement Copy. When assigning them, Rust automatically copies the value, so both names hold independent data.

let x = 5;
let y = x; // A full copy is made.
println!("x: {}, y: {}", x, y); // ✅ Works.
Copied!

String and Vec<T> do not implement Copy, because an implicit copy of their representation would leave two owners of the same allocation. When assigning them, Rust transfers ownership (what we call a move).

Copy does not simply mean “lives on the stack”. A shared reference can be Copy, and a type made only of fixed-size data may not be. What matters is whether the type implements the Copy trait.

A mental picture of ownership

To visualize Ownership, think of Heap memory as a briefcase full of money.

  1. Rule 1: The briefcase must be handcuffed to someone’s wrist (owner).
  2. Rule 2: If you want to give the briefcase to another person, you have to take off the handcuffs and put them on that person. You are left without the briefcase. Two people cannot carry it at the same time.
  3. Rule 3: If the person with the briefcase leaves the room (scope), the briefcase is destroyed.

The ownership system is part of the price to pay for having control over resources with safety guarantees in safe code.

  • It forces us to think: “Who owns this data now?”.
  • It deterministically frees resources tied to their owners.
  • It prevents using already freed memory from safe Rust code.

It may seem restrictive that let s2 = s1 invalidates s1, but in reality, it is saving us from very difficult-to-detect bugs.