Giao diện
Ownership - The Law Core Concept
Mỗi byte trong Rust đều có chủ — và chỉ có MỘT chủ duy nhất
Ba Quy Tắc Vàng của Ownership
📜 THE THREE RULES
- Mỗi value trong Rust có một variable gọi là owner
- Chỉ có thể có MỘT owner tại một thời điểm
- Khi owner ra khỏi scope, value bị dropped (deallocated)
Đây là luật. Không có exceptions. Compiler enforce điều này tại compile-time.
Rule #1: Mỗi Value có Một Owner
rust
fn main() {
let s = String::from("hello"); // s là owner của String "hello"
// ↑
// OWNER
} STACK HEAP
┌─────────────────┐ ┌─────────────────────┐
s: String │ ptr ──────────────────────▶ │ h │ e │ l │ l │ o │
(OWNER) ├─────────────────┤ └─────────────────────┘
│ len: 5 │
├─────────────────┤
│ cap: 5 │
└─────────────────┘Rule #2: Chỉ Một Owner Tại Một Thời Điểm (Move Semantics)
❌ Code SAI: Sử dụng sau khi Move
rust
fn main() {
let s1 = String::from("hello");
let s2 = s1; // Ownership MOVED từ s1 sang s2
println!("{}", s1); // ❌ COMPILE ERROR!
}Compiler Error:
error[E0382]: borrow of moved value: `s1`
--> src/main.rs:5:20
|
2 | let s1 = String::from("hello");
| -- move occurs because `s1` has type `String`
3 | let s2 = s1;
| -- value moved here
4 |
5 | println!("{}", s1);
| ^^ value borrowed here after moveMove Semantics Visualization
TRƯỚC let s2 = s1:
═══════════════════════════════════════════════════════════════════
STACK HEAP
┌─────────────────┐ ┌─────────────────────┐
s1: String │ ptr ──────────────────────▶ │ h │ e │ l │ l │ o │
(OWNER) ├─────────────────┤ └─────────────────────┘
│ len: 5 │
│ cap: 5 │
└─────────────────┘
SAU let s2 = s1:
═══════════════════════════════════════════════════════════════════
STACK HEAP
┌─────────────────┐
s1: String │ ██ INVALID ████ │ ┌─────────────────────┐
(MOVED OUT) │ █████████████████│ ┌───▶│ h │ e │ l │ l │ o │
│ █████████████████│ │ └─────────────────────┘
└─────────────────┘ │
│
┌─────────────────┐ │
s2: String │ ptr ────────────────────┘
(NEW OWNER) ├─────────────────┤
│ len: 5 │
│ cap: 5 │
└─────────────────┘✅ Code ĐÚNG: Clone nếu cần cả hai
rust
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // Deep copy - s2 có data riêng
println!("s1: {}, s2: {}", s1, s2); // ✅ OK
}SAU let s2 = s1.clone():
═══════════════════════════════════════════════════════════════════
STACK HEAP
┌─────────────────┐ ┌─────────────────────┐
s1: String │ ptr ──────────────────────▶ │ h │ e │ l │ l │ o │
(OWNER) ├─────────────────┤ └─────────────────────┘
│ len: 5 │
│ cap: 5 │
└─────────────────┘
┌─────────────────────┐
┌─────────────────┐ ┌────▶│ h │ e │ l │ l │ o │
s2: String │ ptr ────────────────────┘ └─────────────────────┘
(OWNER) ├─────────────────┤ (COPY của data)
│ len: 5 │
│ cap: 5 │
└─────────────────┘Rule #3: Owner Ra Khỏi Scope → Value Dropped
rust
fn main() {
{
let s = String::from("hello"); // s valid từ đây
// ... sử dụng s
} // ← s ra khỏi scope, String::drop() được gọi
// Heap memory được deallocate
// println!("{}", s); // ❌ ERROR: s không tồn tại
}Drop được gọi tự động
rust
fn main() {
let s = String::from("hello");
// ... code ở đây
} // ← Rust tự động gọi drop(s)
// Tương đương: s.drop() được compiler insertSo sánh với C++:
cpp
// C++ - phải nhớ delete
char* s = new char[6];
strcpy(s, "hello");
// ... quên delete s → MEMORY LEAKMove trong Function Calls
❌ Code SAI: Sử dụng sau khi pass vào function
rust
fn takes_ownership(s: String) {
println!("{}", s);
} // s dropped ở đây
fn main() {
let s = String::from("hello");
takes_ownership(s); // Ownership MOVED vào function
println!("{}", s); // ❌ COMPILE ERROR!
}Compiler Error:
error[E0382]: borrow of moved value: `s`
--> src/main.rs:9:20
|
6 | let s = String::from("hello");
| - move occurs because `s` has type `String`
7 | takes_ownership(s);
| - value moved here
8 |
9 | println!("{}", s);
| ^ value borrowed here after moveStack Visualization
TRƯỚC takes_ownership(s):
═══════════════════════════════════════════════════════════════════
main() Stack Frame:
┌─────────────────┐ ┌─────────────────────┐
│ s: String │───────────▶│ h │ e │ l │ l │ o │
│ (OWNER) │ └─────────────────────┘
└─────────────────┘
SAU takes_ownership(s) được gọi:
═══════════════════════════════════════════════════════════════════
takes_ownership() Stack Frame:
┌─────────────────┐ ┌─────────────────────┐
│ s: String │───────────▶│ h │ e │ l │ l │ o │
│ (NEW OWNER) │ └─────────────────────┘
└─────────────────┘
↑
main() Stack Frame:
┌─────────────────┐
│ s: ██ INVALID██ │
│ (MOVED OUT) │
└─────────────────┘✅ Code ĐÚNG: Return ownership về
rust
fn takes_and_gives_back(s: String) -> String {
println!("{}", s);
s // Return ownership về caller
}
fn main() {
let s1 = String::from("hello");
let s2 = takes_and_gives_back(s1); // s1 moved in, ownership returned to s2
println!("{}", s2); // ✅ OK
}Copy Trait vs Drop Trait
Copy Types (Stack-only, Cheap to Copy)
Một số types implement Copy trait → assignment = bitwise copy, không move:
rust
fn main() {
let x: i32 = 5;
let y = x; // x được COPY (không phải move)
println!("x: {}, y: {}", x, y); // ✅ CẢ HAI đều valid
}Types implement Copy:
| Type | Size | Lý do Copy |
|---|---|---|
Tất cả integers (i32, u64, etc.) | Fixed | Stack-only, copy nhanh |
Tất cả floats (f32, f64) | Fixed | Stack-only, copy nhanh |
bool | 1 byte | Trivial |
char | 4 bytes | Trivial |
| Tuples chứa Copy types | Fixed | (i32, f64) là Copy |
| Arrays chứa Copy types | Fixed | [i32; 5] là Copy |
References (&T) | 8 bytes | Pointer copy, không own data |
Drop Types (Heap-involved, Move Semantics)
Types không implement Copy → assignment = Move:
| Type | Lý do Move |
|---|---|
String | Owns heap data |
Vec<T> | Owns heap data |
Box<T> | Owns heap data |
HashMap<K, V> | Owns heap data |
| Custom structs (default) | May contain Drop types |
Quy tắc: Copy và Drop loại trừ nhau
rust
// ❌ KHÔNG THỂ implement cả hai
// Nếu type có destructor (Drop), không thể Copy
struct MyString {
data: String, // String là Drop → MyString cũng là Drop
}
// MyString tự động là Move, không phải CopyCách implement Copy cho custom type
rust
// ✅ Copy nếu tất cả fields đều Copy
#[derive(Copy, Clone)]
struct Point {
x: i32, // Copy
y: i32, // Copy
}
fn main() {
let p1 = Point { x: 5, y: 10 };
let p2 = p1; // Copy, không phải Move
println!("p1: ({}, {})", p1.x, p1.y); // ✅ OK
println!("p2: ({}, {})", p2.x, p2.y); // ✅ OK
}rust
// ❌ KHÔNG THỂ Copy nếu có field là Drop
#[derive(Copy, Clone)] // ❌ COMPILE ERROR!
struct Person {
name: String, // String không phải Copy
age: u32,
}Compiler Error:
error[E0204]: the trait `Copy` may not be implemented for this type
--> src/main.rs:1:10
|
1 | #[derive(Copy, Clone)]
| ^^^^
2 | struct Person {
3 | name: String,
| ------------ this field does not implement `Copy`
}Memory Layout: Copy vs Move
Copy (Bitwise Duplicate)
rust
let x: i32 = 42;
let y = x; // CopyTRƯỚC: SAU (Copy):
┌────────────────┐ ┌────────────────┐
│ x: i32 = 42 │ │ x: i32 = 42 │ ← Vẫn valid
└────────────────┘ └────────────────┘
┌────────────────┐
│ y: i32 = 42 │ ← Copy mới
└────────────────┘Move (Ownership Transfer)
rust
let s1 = String::from("hello");
let s2 = s1; // MoveTRƯỚC: SAU (Move):
┌────────────────┐ ┌────────────────┐
│ s1: ptr ───────┼──▶ "hello" │ s1: INVALID │ ← Không còn valid
└────────────────┘ └────────────────┘
┌────────────────┐
│ s2: ptr ───────┼──▶ "hello" (cùng data)
└────────────────┘Ownership trong Pattern Matching
Move trong match
rust
fn main() {
let opt: Option<String> = Some(String::from("hello"));
match opt {
Some(s) => println!("Got: {}", s), // s takes ownership
None => println!("Nothing"),
}
// println!("{:?}", opt); // ❌ ERROR: opt moved in match
}✅ Avoid move với ref
rust
fn main() {
let opt: Option<String> = Some(String::from("hello"));
match &opt { // Borrow, không move
Some(s) => println!("Got: {}", s), // s là &String
None => println!("Nothing"),
}
println!("{:?}", opt); // ✅ OK - opt không bị move
}Bảng Tóm tắt
| Concept | Ý nghĩa |
|---|---|
| Owner | Variable sở hữu value, chịu trách nhiệm drop |
| Move | Chuyển ownership, biến cũ invalid |
| Copy | Bitwise duplicate, cả hai biến valid |
| Drop | Destructor, cleanup khi owner ra khỏi scope |
| Clone | Explicit deep copy, tốn resource |
Bài tập: Fix Compiler Errors
💪 Bài 1: Fix lỗi move
rust
fn main() {
let s = String::from("hello");
let len = calculate_length(s);
println!("The length of '{}' is {}.", s, len);
}
fn calculate_length(s: String) -> usize {
s.len()
}Đáp án
Cách 1: Return ownership
rust
fn calculate_length(s: String) -> (String, usize) {
let len = s.len();
(s, len)
}
fn main() {
let s = String::from("hello");
let (s, len) = calculate_length(s);
println!("The length of '{}' is {}.", s, len);
}Cách 2: Dùng reference (tốt hơn - sẽ học ở bài sau)
rust
fn calculate_length(s: &String) -> usize {
s.len()
}
fn main() {
let s = String::from("hello");
let len = calculate_length(&s);
println!("The length of '{}' is {}.", s, len);
}💪 Bài 2: Copy hay Move?
Xác định mỗi biến sau khi assignment là Copy hay Move:
rust
let a: i32 = 5;
let b = a; // A
let s1 = String::from("hi");
let s2 = s1; // B
let t1 = (1, 2);
let t2 = t1; // C
let v1 = vec![1, 2, 3];
let v2 = v1; // DĐáp án
- A: Copy —
i32implements Copy - B: Move —
Stringkhông implement Copy - C: Copy — Tuple của Copy types cũng Copy
- D: Move —
Vec<T>không implement Copy