Skip to content

Rust Ownership — The Complete Guide

DodaTech Updated 2026-06-21 7 min read

Rust ownership is a set of rules that governs how memory is managed, ensuring every value has exactly one owner and is automatically freed when that owner goes out of scope.

What You'll Learn

In this tutorial, you'll learn the three rules of Rust ownership, how moves and copies differ, how functions affect ownership, and why ownership eliminates memory bugs at compile time.

Why It Matters

Ownership is Rust's most distinctive feature. It replaces garbage collection and manual memory management with a compile-time system that guarantees no dangling pointers, no double frees, and no memory leaks. This is critical for systems where reliability is paramount — like the Linux kernel, web browsers, and antivirus engines.

Real-World Use

Dropbox's file sync engine uses Rust ownership to manage file handles without leaks. The Tokio async runtime uses ownership to ensure tasks release their resources. Durga Antivirus Pro's signature scanner uses ownership to guarantee every opened file handle is closed, even during early return on error.

flowchart TD
    A[Value Created] --> B{Owner Exists?}
    B -->|Yes| C[Owner controls value]
    C --> D{Value Moved?}
    D -->|Yes| E[New owner acquires control]
    D -->|No| F{Owner goes out of scope?}
    E --> G[Old owner invalidated]
    G --> F
    F -->|Yes| H[Value dropped / memory freed]
    F -->|No| C
â„šī¸ Info

Prerequisites: Rust basics and memory management concepts. Understanding stack vs heap helps.

The Three Ownership Rules

Rule 1: Each value has exactly one owner

The owner is the variable that holds the value. There is never more than one owner at a time.

fn main() {
    let owner = String::from("I own this string");
    println!("Owner says: {}", owner);
} // owner goes out of scope, string is freed

Expected output:

Owner says: I own this string

Rule 2: When the owner goes out of scope, the value is dropped

Rust automatically calls drop() on the value when its owner exits scope. No explicit free() needed.

struct Token {
    id: u32,
}

impl Drop for Token {
    fn drop(&mut self) {
        println!("Token {} is being freed", self.id);
    }
}

fn main() {
    let t = Token { id: 42 };
    println!("Token {} is alive", t.id);
    // t goes out of scope here, drop() runs automatically
}

Expected output:

Token 42 is alive
Token 42 is being freed

Rule 3: Ownership can be transferred (moved)

Assigning a value to another variable moves ownership. The original variable is no longer valid.

fn main() {
    let s1 = String::from("hello");
    let s2 = s1; // s1 is MOVED to s2
    // println!("{}", s1); // ERROR: borrow of moved value
    println!("s2 owns it now: {}", s2);
}

fn process_string(s: String) {
    println!("Processing: {}", s);
} // s is dropped here

fn main2() {
    let name = String::from("Alice");
    process_string(name); // ownership moved into function
    // println!("{}", name); // ERROR: value used after move
}

Expected output:

s2 owns it now: hello
Processing: Alice

Copy vs Move

Simple types that implement the Copy trait are copied instead of moved. These include integers, floats, booleans, and char.

fn main() {
    // Copy types (implement Copy trait)
    let x = 42;
    let y = x; // x is COPIED, not moved
    println!("Both x ({}) and y ({}) are valid", x, y);

    // Move types (do not implement Copy)
    let s1 = String::from("hello");
    let s2 = s1; // s1 is MOVED
    println!("s2 = {}", s2);
    // println!("s1 = {}", s1); // ERROR

    // Tuples of Copy types are also Copy
    let pair = (1, true);
    let pair2 = pair; // copied
    println!("Both pair ({:?}) and pair2 ({:?}) valid", pair, pair2);
}

Expected output:

Both x (42) and y (42) are valid
s2 = hello
Both pair ((1, true)) and pair2 ((1, true)) valid

Functions and Ownership

Passing a value to a function transfers ownership. Returning a value transfers ownership back.

fn take_and_give(s: String) -> String {
    println!("Got: {}", s);
    let new_s = format!("{} World", s);
    new_s // ownership returned to caller
}

fn main() {
    let s1 = String::from("Hello");
    let s2 = take_and_give(s1); // ownership transferred, then returned
    println!("Back in main: {}", s2);
    // s2 is dropped here
}

Expected output:

Got: Hello
Back in main: Hello World

Ownership and Security Implications

Rust's ownership model directly prevents security vulnerabilities common in C and C++:

  • No buffer overflows: Out-of-bounds access is caught at runtime or compile time
  • No use-after-free: The compiler prevents accessing freed memory
  • No double free: Each value has exactly one owner who drops it once
  • No memory leaks: All resources are freed when their owner scopes end

This makes Rust the preferred language for security-critical systems. Durga Antivirus Pro's on-access scanner processes hundreds of files per second with zero memory safety bugs precisely because of Rust's ownership guarantees.

Common Mistakes

1. Using a Value After Move

Once ownership is moved, the original variable is invalid. Accessing it causes a compile error. Solution: use references instead of moving, or clone the value if you need both copies.

2. Confusing Copy and Move

Primitive types are copied automatically. Heap-allocated types like String and Vec are moved. Know which types implement Copy.

3. Not Returning Ownership from Functions

If a function takes ownership and you need it back, return it. Alternatively, use references for borrowed access.

4. Unnecessary Cloning

Overusing .clone() to avoid ownership issues defeats Rust's performance. Prefer references and borrowing instead.

5. Ignoring the Drop Implementation

Not implementing Drop for types that manage system resources (files, sockets, locks) can cause resource leaks. Always implement Drop for resource-holding types.

Practice Questions

1. What are the three rules of Rust ownership? Each value has one owner. When the owner goes out of scope, the value is dropped. Ownership can be transferred (moved) to another owner.

2. What is the difference between Copy and Move? Copy types (integers, floats, bools) are duplicated on assignment. Move types (String, Vec, Box) transfer ownership, invalidating the original variable.

3. What happens to a value when it is passed to a function? Ownership transfers to the function parameter. The original variable can no longer access the value unless the function returns ownership.

4. How does ownership improve security? It prevents use-after-free, double free, memory leaks, and buffer overflows at compile time. These bugs account for most critical CVEs in C/C++ programs.

5. Challenge: Write a function that takes ownership of a Vec<i32>, doubles each element, and returns the vector. Verify the original variable is no longer valid.

Mini Project: Safe File Reader with Ownership

use std::fs::File;
use std::io::{self, Read};

struct SafeFile {
    contents: String,
}

impl SafeFile {
    fn open(path: &str) -> io::Result<SafeFile> {
        let mut file = File::open(path)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        println!("File loaded, {} bytes read", contents.len());
        Ok(SafeFile { contents })
    }

    fn take_contents(self) -> String {
        self.contents // ownership moves to caller
    }
}

fn main() -> io::Result<()> {
    let f = SafeFile::open("Cargo.toml")?;
    let data = f.take_contents(); // ownership transferred
    // println!("{}", f.contents); // ERROR: f is consumed
    println!("File starts with: {}", &data[..data.len().min(80)]);
    Ok(())
}

FAQ

What is the owner of a value in Rust?

The owner is the variable that holds the value. Only one owner exists at a time. When the owner goes out of scope, Rust automatically calls drop on the value, freeing its resources.

What types implement the Copy trait?

Primitive types: integers, floats, booleans, char, and tuples containing only Copy types. Types with heap allocation (String, Vec, Box) do not implement Copy because copying would require allocating new memory.

Can ownership be returned from a function?

Yes. If a function takes ownership of a value and returns it, ownership transfers back to the caller. This is common with methods like String::into_bytes() that consume the string and return the underlying bytes.

Memory Management
Borrowing & References
Rust Lifetimes

What's Next

Master Borrowing & References to learn how to access data without taking ownership, then study Lifetimes for advanced reference safety.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro