Rust: Rust Ownership

Last updated: 2026-08-26

Ownership is Rust's most distinctive feature—it allows Rust to guarantee memory safety without the need for a garbage collector.

Ownership is the most fundamental difference between Rust and other languages. Once you understand it, you understand the essence of Rust's design.


1. What You'll Learn



2. Conceptual Diagrams

100%
flowchart LR
    A["Value Creation<br>let s = String::from(...)"] --> B["Owner Binding<br>s Has this value"]
    B --> C["Transfer of Ownership move<br>let s2 = s; s Failure"]
    C --> D["Out of scope drop<br>} Automatic Memory Release"]
    D --> E["Memory Security<br>No Double Release,Non-swinging pointer"]


3. The Story of a Library

(1) The Agony: The Book Was Borrowed by Two People at the Same Time

Tom manages a small library. He encountered a classic problem:

"If only a book could be borrowed by just one person at a time..."

(2) Rust Ownership Rules

RUST
fn main() {
    let book = String::from("Rust Programming");  // book is the owner of this book
    // let book2 = book;                   // ❌ If you write it this way,book Ownership was transferred to book2
    // println!("{}", book);               // ❌ book Can no longer be used

    // ✅ The Correct Approach:Only one person can hold ownership at a time.
    println!("{} Belongs to Tom The Library", book);
}  // book Automatically destroyed here——No manual intervention required free

Rust's ownership rules are like a library's "one person at a time" policy: Every value has exactly one owner at any given time. When the owner leaves the scope, the value is automatically destroyed.



4. The Three Principles of Ownership

100%
graph TB
    A[The Three Principles of Ownership] --> B[Principles1: Each value has exactly one owner.]
    A --> C[Principles2: It is destroyed as soon as it leaves its scope.]
    A --> D[Principles3: Ownership can be transferred(move)]
    B --> E[let s = String::from("hi")]
    C --> F[} Automatically call at the end drop]
    D --> G[let s2 = s;  // s No longer valid]

(1) Detailed Explanation of the Principles

Principle Description Analogy
Single Owner At any given time, only one person "owns" each value A library book can only be checked out by one person at a time
End of Scope = Destroy Automatically called when a variable goes out of scope drop Books are automatically returned when they are due
Transfer of Ownership (move) Ownership is transferred during assignment or parameter passing; the original variable becomes invalid A book is passed from one person to another


5. Stacks and Heaps

To understand ownership, you must first understand the difference between the stack and the heap:

Dimension Stack Heap
Allocation Speed Very fast (push/pop) Slower (requires finding free memory)
Data Storage Data of a known size at compile time Data of unknown or dynamically changing size
Typical Types i32,bool,f64,Array String,Vec,Box
Memory Management Automatic (function call stack frames) Requires manual management or ownership-based management

(3) Copy vs Clone vs Move Comparison

Operation Syntax Is the original variable valid? Performance overhead Applicable types
Copy let b = a; Valid Very low (bitwise copy of the stack) i32, f64, bool, char, tuples (including Copy types)
Clone let b = a.clone(); Valid High (stack memory allocation) String, Vec<T>, Box<T>
Move let b = a; Expires 0 (only copies the pointer) String, Vec<T>, Box<T>

If a type implements Copy, assignment results in an automatic copy; if it does not implement Copy, assignment results in an automatic move. Use .clone() for an explicit deep copy when you need to preserve the original variable.

RUST
fn main() {
    // Data on the Stack:Fixed size,Copy Semantics
    let x: i32 = 5;        // Allocation on the Stack 4 Byte
    let y = x;              // Make a copy, x and y are both valid.
    println!("x={}, y={}", x, y);  // ✅ All are acceptable

    // Load the data:Varies in size,move Semantics
    let s1 = String::from("hello");  // s1 On the stack(ptr/len/cap),The actual data is on the heap.
    let s2 = s1;                     // ❌ Ownership from s1 Transfer to s2
    // println!("{}", s1);            // ❌ Compilation Error:s1 Has been moved
    println!("{}", s2);              // ✅ s2 It's the new owner.
}


6. Examples of Ownership

▶ Example 1: "move" semantics—transfer of ownership (Difficulty ⭐⭐)

Output:

TEXT 📖 Display only
s1: Rust
s2: Rust
a: 42, b: 42
RUST
// ============================================
// Demonstration of Ownership Transfer:Occurs during assignment move
// ============================================

fn main() {
    let s1 = String::from("Rust");
    let s2 = s1;  // s1 Ownership was transferred to s2

    // println!("s1: {}", s1);  // ❌ Compilation Error!s1 Has been moved
    println!("s2: {}", s2);     // ✅ s2 I am the owner now

    // Integer types are Copy, So it won't move
    let a = 42;
    let b = a;                 // a The value is copied to b
    println!("a: {}, b: {}", a, b);  // ✅ Both are effective.
}

Output:

TEXT 📖 Display only
s2: Rust
a: 42, b: 42

Output:

TEXT 📖 Display only
Acquire ownership: Newly created string

Scalar types such as integers implement the Copy trait, so assignments involve copying rather than moving. String does not implement Copy, so assignments involve moving. After a move, the original variable becomes invalid; this is a key design feature in Rust to prevent "double free."


▶ Example 2: Passing Ownership in Function Calls (Difficulty ⭐⭐)

Output:

TEXT 📖 Display only
Acquire ownership: Newly created string
hello
s2: <s2>
Passing back and forth
s3_back: <s3_back>
Inside a function: Newly created string
RUST
// ============================================
// Transfer of Ownership in Parameter Passing and Return Values
// ============================================

fn take_ownership(s: String) {
    println!("Acquire ownership: {}", s);
}  // s Destroyed here(drop)

fn give_ownership() -> String {
    let s = String::from("Newly created string");
    s  // Ownership is returned to the caller
}

fn main() {
    let s1 = String::from("hello");

    take_ownership(s1);  // s1 Ownership was transferred to the function
    // println!("{}", s1);  // ❌ s1 Expired

    let s2 = give_ownership();  // Acquiring Ownership from a Function
    println!("s2: {}", s2);     // ✅ s2 Ownership

    // Send it in and then send it back
    let s3 = String::from("Passing back and forth");
    let s3_back = takes_and_returns(s3);
    // println!("{}", s3);  // ❌ s3 Has been moved
    println!("s3_back: {}", s3_back);  // ✅
}

fn takes_and_returns(s: String) -> String {
    println!("Inside a function: {}", s);
    s  // Return ownership to the caller
}

Output:

TEXT 📖 Display only
Acquire ownership: hello
s2: Newly created string
Inside a function: Passing back and forth
s3_back: Passing back and forth

When a function is called, ownership of the passed-in parameters is transferred to the function. When the function returns a value, ownership is transferred back to the caller. This is what is meant by "ownership flowing between functions"—it's the same principle as "whoever holds the key can open the door."


▶ Example 3: Comparing "Copy" and "Clone" (Difficulty: ⭐⭐⭐)

Output:

TEXT 📖 Display only
Copy: num1=100, num2=100
Clone: s1=Cloning required, s2=<s2>
[1, 2, 3]
v2: [1, 2, 3]
RUST
// ============================================
// Copy(Auto-Copy) vs Clone(Explicit Cloning)
// ============================================

fn main() {
    // --- Copy Type:Automatic Copy on Assignment ---
    let num1 = 100;
    let num2 = num1;         // Auto-Copy,num1 Still valid
    println!("Copy: num1={}, num2={}", num1, num2);  // ✅

    // --- Clone Type:Must be explicitly called .clone() ---
    let s1 = String::from("Cloning required");
    let s2 = s1.clone();     // Explicit Cloning,s1 Still valid
    println!("Clone: s1={}, s2={}", s1, s2);  // ✅

    // --- Neither Clone Nor Copy: only move ---
    let v1 = vec![1, 2, 3];
    let v2 = v1;  // move!v1 Failure
    // println!("{:?}", v1);  // ❌
    println!("v2: {:?}", v2);  // ✅
}

Output:

TEXT 📖 Display only
Before the upgrade: <format_user(&alice)>
After the upgrade: <format_user(&alice)>
Upgrade Again: <format_user(&alice)>
Bob After the bonus points are added: <format_user(&bob)>
All users: <names>
Alice Final Score: <alice.score>
Bob Final Score: <bob.score>

Copy: Automatically performs a bit-by-bit copy during assignment; the original variable remains valid (applies to data on the stack). Clone: Requires an explicit call to the .clone() method; applies to data on the heap. Cloning is more expensive (it requires allocating heap memory), so Rust is designed such that "you must explicitly choose to clone."


▶ Example 4: Patterns for Acquiring and Returning Ownership in Functions (Difficulty ⭐⭐⭐)

Output:

TEXT 📖 Display only
Before the upgrade: <format_user(&alice)>
After the upgrade: <format_user(&alice)>
Upgrade Again: <format_user(&alice)>
Bob After the bonus points are added: <format_user(&bob)>
All users: <names>
Alice Final Score: <alice.score>
Bob Final Score: <bob.score>
RUST
// ============================================
// Common Patterns:Send it in and then send it back(Get+Return)
// ============================================

struct User {
    name: String,
    score: i32,
}

fn boost_score(mut user: User, bonus: i32) -> User {
    user.score += bonus;
    user
}

fn format_user(user: &User) -> String {
    format!("{}: {} pts", user.name, user.score)
}

fn main() {
    let alice = User {
        name: String::from("Alice"),
        score: 80,
    };

    println!("Before the upgrade: {}", format_user(&alice));

    let alice = boost_score(alice, 15);
    println!("After the upgrade: {}", format_user(&alice));

    let alice = boost_score(alice, 10);
    println!("Upgrade Again: {}", format_user(&alice));

    let mut bob = User {
        name: String::from("Bob"),
        score: 60,
    };

    let bonus = 20;
    bob.score += bonus;
    println!("Bob After the bonus points are added: {}", format_user(&bob));

    let names = vec![alice.name.clone(), bob.name.clone()];
    println!("All users: {:?}", names);

    println!("Alice Final Score: {}", alice.score);
    println!("Bob Final Score: {}", bob.score);
}

Output:

TEXT 📖 Display only
Before the upgrade: Alice: 80 pts
After the upgrade: Alice: 95 pts
Upgrade Again: Alice: 105 pts
Bob After the bonus points are added: Bob: 80 pts
All users: ["Alice", "Bob"]
Alice Final Score: 105
Bob Final Score: 80

Ownership of structures is transferred in the same way as for basic types. boost_score Ownership is acquired through a parameter and returned after modification—this is the "acquire-and-return" pattern. Using a reference (&User) allows read-only access without acquiring ownership.


▶ Example 5: Interaction Between Ownership and Sets (Difficulty ⭐⭐⭐)

Output:

TEXT 📖 Display only
Original vector: <words>
The First Word: <first>
Remove Index 1: <removed>, Remaining: <words>
Pop up: <val>
The vector is empty: [10, 20, 30, 40, 50]
Stitching Results: foobar
Borrow: <word>
The vector is still available: [a, b]
Consumption: <num>
RUST
// ============================================
// Vec,String Common Interaction Patterns with Ownership
// ============================================

fn take_first_word(words: &Vec<String>) -> Option<&str> {
    words.first().map(|s| s.as_str())
}

fn remove_and_return(vec: &mut Vec<i32>, index: usize) -> Option<i32> {
    if index < vec.len() {
        Some(vec.remove(index))
    } else {
        None
    }
}

fn main() {
    let mut words = vec![
        String::from("hello"),
        String::from("rust"),
        String::from("world"),
    ];
    println!("Original vector: {:?}", words);

    if let Some(first) = take_first_word(&words) {
        println!("The First Word: {}", first);
    }

    let removed = remove_and_return(&mut words, 1);
    println!("Remove Index 1: {:?}, Remaining: {:?}", removed, words);

    let mut numbers = vec![10, 20, 30, 40, 50];
    while let Some(val) = numbers.pop() {
        println!("Pop up: {}", val);
    }
    println!("The vector is empty: {:?}", numbers);

    let s1 = String::from("foo");
    let s2 = String::from("bar");
    let combined = s1 + &s2;
    println!("Stitching Results: {}", combined);

    let data = vec![String::from("a"), String::from("b")];
    for word in &data {
        println!("Borrow: {}", word);
    }
    println!("The vector is still available: {:?}", data);

    let data2 = vec![1, 2, 3];
    for num in data2 {
        println!("Consumption: {}", num);
    }
}

Output:

TEXT 📖 Display only
Original vector: ["hello", "rust", "world"]
The First Word: hello
Remove Index 1: Some(20), Remaining: ["hello", "world"]
Pop up: 50
Pop up: 40
Pop up: 30
Pop up: 20
Pop up: 10
The vector is empty: []
Stitching Results: foobar
Borrow: a
Borrow: b
The vector is still available: ["a", "b"]
Consumption: 1
Consumption: 2
Consumption: 3

for item in &vec Borrow traversal (the vector remains available), for item in vec Consume traversal (the vector is moved). + The operator consumes the String on the left. pop() Returns Option<T> and consumes the last element.


❓ FAQ

Q Why was ownership designed in Rust? Wouldn't it be simpler to use garbage collection?
A Ownership allows Rust to guarantee memory safety without GC pauses.
Q Where does the original variable go after a move?
A The original variable is marked as "invalid" by the compiler.
Q Which types are copyable?
A All scalar types (integers, floating-point numbers, booleans, characters) and tuples/arrays composed of them.
Q Which has better performance, .clone() or move?
A move has zero overhead, while clone requires allocating heap memory.
Q How does ownership work with structs?
A The ownership rules for a struct as a whole are the same as for a single variable.

📖 Summary


📝 Exercises

  1. Difficulty ⭐: Write a program that creates a String, assigns it to another variable, and then attempts to print the first variable—observe the compiler's error message.
  2. Difficulty ⭐⭐: Write a function fn append_world(s: String) -> String that appends " world" to the end of a string and returns it. Call it in main and observe how ownership is passed into and out of the function.
  3. Difficulty ⭐⭐⭐: Define a function fn calculate_length(s: String) -> (String, usize) that returns a string and its length, while returning ownership of the string. Verify that the original variable can still be used after the function is called.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏