Rust: Ownership, Borrowing (References), Lifetimes, Move/Cop

Leon-avatar-img
發佈於Rust
更新於 發佈於 閱讀時間約 20 分鐘

🧠 Why Care About These Concepts?

  • Reliability These concepts allow the Rust compiler to prevent common resource errors such as:
    • Memory leaks
    • Dangling pointers
    • Double frees
    • Accessing uninitialized memory
  • Convenience The Rust compiler can automatically free resources using these concepts — no need for manual free or a garbage collector.
  • Performance Resource management without a garbage collector enables:
    • Faster performance
    • Suitability for real-time systems

🛠️ How to Use These Features?

  • These features are enforced by the compiler through compile-time checks.
  • You don't usually do something manually — instead, you follow Rust's rules.
  • Understanding these concepts is essential to fix compiler error messages during development.

🧾 Basic Memory Management Terminology

  • A variable is a name for a memory location holding a value of some type.
  • Memory can be allocated in three regions:
    • Stack: Automatically allocated when a function is called, and freed when the function returns.
    • Heap: Requires explicit allocation and deallocation (handled by Rust’s ownership model).
    • Static memory: Lives for the entire duration of the program.

🐞 Common Memory Errors

  • Dangling Pointer: A pointer to memory that has been freed or was never initialized.
  • Memory Leak: Memory allocated on the heap is never freed — e.g., forgetting to release memory.
  • Uninitialized Memory: Using memory before it has been properly allocated or assigned a value.
  • Double Free: Attempting to free the same memory more than once — either on the same variable or on a copy.

⚠️ What Can Go Wrong on the Stack?

  • Since memory is automatically allocated and freed, there are no memory leaks, uninitialized memory, or double free problems.
  • The function might return a pointer to a value on the stack, leading to a dangling pointer.
  • Rust prevents this by simply checking that no such references are returned — see stack-dangling-pointer.rs.

❌ Example: Returning a reference to a local variable

fn create_ref() -> &i32 {
let number = 10;
&number // ❌ This will cause a compile error
}

Compiler Error:

error[E0515]: cannot return reference to local variable `number`

  • Don’t return references to local function variables — copy or move the value out of the function.

✅ Correct version: Move the value out

fn create_value() -> i32 {
let number = 10;
number // ✅ Move the value instead of returning a reference
}


💾 What Can Go Wrong on the Heap?

  • A reference might be used after the memory was reallocated or freed, leading to a dangling pointer.
  • The borrow checker prevents the reallocation by not allowing a mutable and other reference at the same time. An immutable reference is needed to push on a vector and possibly reallocate it — see heap-reallocation-dangling-pointer.rs.

❌ Example: Reallocation with active immutable borrow

fn main() {
let mut vec = vec![1, 2, 3];
let first = &vec[0]; // Immutable borrow
vec.push(4); // ❌ May cause reallocation
println!("{}", first); // Use after potential reallocation
}

Compiler Error:

error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable

  • Don’t do it, and if you really need a mutable reference paired with other references, use the std::cell module.

✅ Correct version: Borrow after mutation

fn main() {
let mut vec = vec![1, 2, 3];
vec.push(4); // ✅ Mutate first
let first = &vec[0]; // Borrow afterwards
println!("{}", first);
}


  • Rust prevents the use after free case by making sure no reference is used after its lifetime has ended, i.e. the value was dropped — see heap-dropped-dangling-pointer.rs.

❌ Example: Reference lives longer than the value

fn main() {
let r;
{
let vec = vec![1, 2, 3];
r = &vec[0]; // ❌ `vec` goes out of scope here
}
println!("{}", r); // Use after drop
}

Compiler Error:

error[E0597]: `vec` does not live long enough

  • Don’t do it, or if you really run into this problem, you might need shared ownership with the std::rc module.

  • The borrow checker also does not allow a value to be moved to another variable that could reallocate or free the memory while there are references — see heap-move-dangling-pointer.rs.

❌ Example: Move after borrow

fn main() {
let vec = vec![1, 2, 3];
let r = &vec;
let moved = vec; // ❌ Moving vec while r still exists
println!("{:?}", r);
}

Compiler Error:

error[E0505]: cannot move out of `vec` because it is borrowed

  • Don’t move a value to another variable and then use a reference to it you created before.

✅ Correct version: Use after move or avoid borrowing before move

fn main() {
let vec = vec![1, 2, 3];
let moved = vec; // ✅ Move without borrowing first
println!("{:?}", moved);
}


🔑 What Is Ownership?

  • Rust automatically frees memory, so there are no memory leaks or double free calls.
  • Rust does this without a garbage collector.

🧹 How Does Rust Manage Memory?

  • The concept is simple: Rust calls a destructor (drop) whenever the lifetime of a value ends, i.e., when the value goes out of scope ({} block ends).
fn main() {
{
let _s = String::from("hello");
// `_s` is dropped here automatically
}
// memory is freed
}


❌ The Problem with Shallow Copies

  • Some values, like a Vec, contain heap-allocated data.
  • A shallow copy (only copying pointer and metadata) would cause a double free error if both copies tried to free the same memory.

✅ Rust prevents this with Move Semantics

  • When you assign one variable to another, Rust moves the value instead of copying it (unless it implements Copy).

🔁 Move Example (move-semantics.rs)

fn main() {
let vec = vec![1, 2, 3];
let moved = vec; // vec is moved
// println!("{:?}", vec); // ❌ error: use of moved value
}

🧠 Compiler Error:

error[E0382]: borrow of moved value: `vec`

  • Don’t use a value after it was moved.
  • If you really need a deep copy, use .clone():
fn main() {
let vec = vec![1, 2, 3];
let cloned = vec.clone(); // deep copy
println!("{:?}", vec); // ✅ ok to use
}

⚠️ .clone() is often unnecessary and can lead to performance issues if overused.


📦 Copy vs Move

  • Primitive types (e.g., i32, bool, char) are copied by default.
    • No heap data → no double free risk.
fn main() {
let a = 42;
let b = a; // a is copied, not moved
println!("a = {}, b = {}", a, b); // ✅ both are usable
}

  • Types with heap data (e.g., Vec, String) are moved by default.
  • You can make your own types Copy-able by implementing the Copy trait:

Example: Copy trait (copy-semantics.rs)

#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}

fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // p1 is copied, not moved
println!("p1: {}, {}", p1.x, p1.y); // ✅ still valid
}

  • If a type is not Copy, Rust will give an error like:
move occurs because `config` has type `Config`, which does not implement the `Copy` trait

✅ Don’t implement Copy if you can live with using references.

✅ You can also just implement Clone and call .clone() explicitly — see clone-semantics.rs.


📌 Summary and Miscellaneous Info

  • Ownership, borrowing, and lifetimes enable the Rust compiler to:
    • Detect and prevent memory errors
    • Handle memory automatically and safely
  • Safe Rust guarantees memory safety — no undefined behavior from memory misuse.
  • You can use unsafe Rust inside an unsafe {} block if needed.
  • Rust understands how to free memory even in:
    • Loops
    • if/match clauses
    • Iterators
    • Partial moves from structs
  • ✅ If your program compiles, you get these memory safety guarantees without a runtime garbage collector.
留言
avatar-img
留言分享你的想法!
avatar-img
Leon-Dev
0會員
1內容數
日常programming logs
你可能也想看
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
創作者營運專員/經理(Operations Specialist/Manager)將負責對平台成長及收入至關重要的 Partnership 夥伴創作者開發及營運。你將發揮對知識與內容變現、影響力變現的精準判斷力,找到你心中的潛力新星或有聲量的中大型創作者加入 vocus。
Thumbnail
這份文件的目的是介紹Swift語言,包括它的特性、應用範疇,以及誰在使用它。它也提供了一些學習Swift的資源和工具,以及一些常見的Swift庫和框架。
Thumbnail
這份文件的目的是介紹Swift語言,包括它的特性、應用範疇,以及誰在使用它。它也提供了一些學習Swift的資源和工具,以及一些常見的Swift庫和框架。
Thumbnail
本章節的目的是介紹在TypeScript中如何進行例外處理。涵蓋了例外處理的重要性、語法、常見異常類型以及如何主動觸發異常訊息及用戶自定義異常訊息。為讀者提供了全面而深入的了解,以提高程式的可靠性、提供更好的反饋、增加程式的容錯性以及改善程式的可讀性。
Thumbnail
本章節的目的是介紹在TypeScript中如何進行例外處理。涵蓋了例外處理的重要性、語法、常見異常類型以及如何主動觸發異常訊息及用戶自定義異常訊息。為讀者提供了全面而深入的了解,以提高程式的可靠性、提供更好的反饋、增加程式的容錯性以及改善程式的可讀性。
Thumbnail
註解 & Print & 變數型態
Thumbnail
註解 & Print & 變數型態
Thumbnail
當你在開發程式時,難免會遇到各種錯誤和異常情況。這些錯誤可能是因為代碼中的錯誤、外部資源無法訪問或其他不可預期的狀況。為了提高程式的可靠性、穩定性和可維護性,我們使用「例外處理」來處理這些異常情況。
Thumbnail
當你在開發程式時,難免會遇到各種錯誤和異常情況。這些錯誤可能是因為代碼中的錯誤、外部資源無法訪問或其他不可預期的狀況。為了提高程式的可靠性、穩定性和可維護性,我們使用「例外處理」來處理這些異常情況。
Thumbnail
※ OPP第一大核心-封裝 封裝的精神在於將「方法」、「屬性」和「邏輯」包裝在類別裡面,透過類別的實例來實現。這樣外部物件不需要了解內部的實現細節,只需要知道如何使用該類別提供的接口即可。換句話說,封裝是將內部細節隱藏起來,只暴露必要的部分給使用者。 封裝的核心概念是,使用者如果想要接觸資料,只
Thumbnail
※ OPP第一大核心-封裝 封裝的精神在於將「方法」、「屬性」和「邏輯」包裝在類別裡面,透過類別的實例來實現。這樣外部物件不需要了解內部的實現細節,只需要知道如何使用該類別提供的接口即可。換句話說,封裝是將內部細節隱藏起來,只暴露必要的部分給使用者。 封裝的核心概念是,使用者如果想要接觸資料,只
Thumbnail
CSS 的繼承性是開發網頁樣式時的一個重要概念,它使得樣式設計更加靈活和高效,有助於提高程式碼的可讀性、一致性和可重用性,並加快開發速度,從而提供更好的開發體驗。
Thumbnail
CSS 的繼承性是開發網頁樣式時的一個重要概念,它使得樣式設計更加靈活和高效,有助於提高程式碼的可讀性、一致性和可重用性,並加快開發速度,從而提供更好的開發體驗。
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
Thumbnail
軟體系統的發展歷程大多相似,首重解決基本需求、提供操作介面,進而提升安全性、擴充功能、優化操作。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News