Ownership và Borrowing trong Rust: Hướng dẫn toàn diện cho phỏng vấn kỹ thuật
Tìm hiểu sâu về hệ thống Ownership và Borrowing trong Rust — từ khái niệm cơ bản đến các pattern nâng cao giúp lập trình viên tự tin vượt qua phỏng vấn kỹ thuật.

Rust nổi tiếng với khả năng đảm bảo an toàn bộ nhớ mà không cần garbage collector. Điều này đạt được nhờ vào hệ thống Ownership và Borrowing — hai khái niệm cốt lõi mà bất kỳ lập trình viên Rust nào cũng cần nắm vững, đặc biệt khi chuẩn bị cho các buổi phỏng vấn kỹ thuật.
Bài viết này sẽ đi sâu vào từng khái niệm, minh họa bằng code cụ thể, và chỉ ra những lỗi phổ biến mà ứng viên thường gặp trong phỏng vấn.
Trong các buổi phỏng vấn Rust, nhà tuyển dụng thường yêu cầu ứng viên giải thích sự khác biệt giữa move, clone và borrow. Việc hiểu rõ ba khái niệm này sẽ tạo lợi thế lớn.
Ownership là gì?
Trong Rust, mỗi giá trị có duy nhất một owner (chủ sở hữu). Khi owner ra khỏi phạm vi (scope), giá trị sẽ tự động được giải phóng. Đây là quy tắc nền tảng giúp Rust loại bỏ hoàn toàn các lỗi như use-after-free hay double-free mà không cần garbage collector.
Ba quy tắc ownership cơ bản:
- Mỗi giá trị trong Rust có một owner duy nhất.
- Tại mỗi thời điểm, chỉ có một owner.
- Khi owner ra khỏi scope, giá trị bị drop (giải phóng).
Move Semantics
Khi gán một giá trị kiểu heap (như String) cho biến khác, ownership được chuyển giao (move). Biến ban đầu không còn hợp lệ sau phép gán này.
fn main() {
let original = String::from("interview prep");
let moved = original; // ownership transfers here
// println!("{}", original); // compile error: value moved
println!("{}", moved); // works fine
}Đây là điểm khác biệt lớn so với các ngôn ngữ như Java hay Python, nơi việc gán chỉ tạo thêm một tham chiếu đến cùng một đối tượng. Trong Rust, sau khi move, biến gốc hoàn toàn không thể truy cập được nữa.
Copy và Clone
Một số kiểu dữ liệu đơn giản (như i32, f64, bool) implement trait Copy, cho phép sao chép tự động khi gán. Với các kiểu phức tạp hơn, lập trình viên cần gọi .clone() để tạo bản sao sâu (deep copy).
fn main() {
let x: i32 = 42;
let y = x; // copy, not move -- i32 is Copy
println!("x = {}, y = {}", x, y); // both valid
let s1 = String::from("hello");
let s2 = s1.clone(); // explicit deep copy
println!("s1 = {}, s2 = {}", s1, s2); // both valid after clone
}Phương thức .clone() tạo một bản sao hoàn toàn độc lập trên heap. Trong các ứng dụng yêu cầu hiệu năng cao, việc sử dụng clone không cần thiết có thể gây ra overhead đáng kể. Hãy ưu tiên sử dụng borrowing thay vì clone khi có thể.
Borrowing: Mượn thay vì sở hữu
Không phải lúc nào cũng cần chuyển giao ownership. Rust cung cấp cơ chế borrowing (mượn) thông qua references, cho phép truy cập giá trị mà không chiếm quyền sở hữu.
Immutable Borrowing
Một immutable reference (&T) cho phép đọc giá trị mà không thay đổi nó. Có thể tồn tại nhiều immutable references cùng lúc, vì việc đọc dữ liệu đồng thời là an toàn.
fn calculate_length(s: &String) -> usize {
s.len() // read access through the reference
} // s goes out of scope, but doesn't drop the String (not the owner)
fn main() {
let greeting = String::from("hello, Rust");
let len = calculate_length(&greeting); // borrow, don't move
println!("'{}' has {} characters", greeting, len); // greeting still valid
}Điểm then chốt ở đây: hàm calculate_length chỉ mượn biến greeting thông qua reference &String. Sau khi hàm kết thúc, greeting vẫn thuộc về hàm main và hoàn toàn có thể sử dụng tiếp.
Mutable Borrowing
Khi cần thay đổi giá trị thông qua reference, Rust yêu cầu sử dụng mutable reference (&mut T). Tuy nhiên, quy tắc nghiêm ngặt được áp dụng: tại mỗi thời điểm, chỉ được phép tồn tại một mutable reference duy nhất.
fn append_greeting(s: &mut String) {
s.push_str(", welcome to Rust!"); // modify through mutable ref
}
fn main() {
let mut message = String::from("Hello");
append_greeting(&mut message);
println!("{}", message); // "Hello, welcome to Rust!"
}Quy tắc độc quyền (Exclusivity Rule)
Rust áp dụng quy tắc nghiêm ngặt để ngăn chặn data race tại thời điểm biên dịch:
- Nhiều immutable references (
&T) có thể tồn tại đồng thời. - Chỉ một mutable reference (
&mut T) được phép tồn tại tại một thời điểm. - Không thể có immutable reference và mutable reference cùng lúc.
fn main() {
let mut data = String::from("shared state");
let r1 = &mut data;
// let r2 = &mut data; // compile error: second mutable borrow
println!("{}", r1);
// After r1's last usage, a new mutable borrow is allowed
let r3 = &mut data; // this works -- non-lexical lifetimes
r3.push_str(" updated");
println!("{}", r3);
}Nhờ tính năng Non-Lexical Lifetimes (NLL), compiler Rust đủ thông minh để nhận biết khi nào một reference không còn được sử dụng, cho phép tạo mutable reference mới ngay sau đó.
Sẵn sàng chinh phục phỏng vấn Rust?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Lifetime: Thời gian sống của reference
Lifetime là cơ chế giúp Rust đảm bảo rằng mọi reference luôn trỏ đến dữ liệu hợp lệ. Trong nhiều trường hợp, compiler tự động suy luận lifetime. Tuy nhiên, khi hàm trả về reference phụ thuộc vào nhiều tham số đầu vào, lập trình viên cần khai báo lifetime tường minh.
Lifetime Annotation cơ bản
// 'a declares: the returned reference lives as long as
// the shortest-lived input reference
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let result;
let string1 = String::from("Rust ownership");
{
let string2 = String::from("borrowing");
result = longest(string1.as_str(), string2.as_str());
println!("Longest: {}", result); // valid: both strings alive
}
// println!("{}", result); // would fail: string2 dropped
}Annotation 'a không thay đổi thời gian sống thực tế của bất kỳ giá trị nào. Nó chỉ mô tả mối quan hệ giữa các lifetime, giúp compiler kiểm tra tính hợp lệ tại thời điểm biên dịch.
Lifetime trong Struct
Khi một struct chứa reference, nó bắt buộc phải khai báo lifetime parameter. Điều này đảm bảo rằng dữ liệu mà struct mượn sẽ luôn tồn tại lâu hơn chính struct đó.
struct Excerpt<'a> {
text: &'a str, // this struct borrows a string slice
}
impl<'a> Excerpt<'a> {
fn summary(&self) -> &str {
let end = self.text.len().min(20);
&self.text[..end] // return a slice of the borrowed text
}
}
fn main() {
let article = String::from("Rust ownership model eliminates memory bugs");
let excerpt = Excerpt {
text: article.as_str(),
};
println!("Summary: {}", excerpt.summary());
}Nhiều ứng viên nhầm lẫn rằng lifetime annotation tạo ra hoặc kéo dài thời gian sống của một giá trị. Thực tế, annotation chỉ là công cụ để compiler kiểm tra — nó không ảnh hưởng đến runtime.
Các pattern Ownership thường gặp trong thực tế
Trong dự án thực tế và các bài phỏng vấn, ba pattern sau xuất hiện thường xuyên nhất:
- Nhận ownership, trả về giá trị mới — phù hợp khi hàm cần biến đổi dữ liệu và trả về kết quả.
- Mượn bất biến — lý tưởng cho các hàm chỉ đọc, kiểm tra dữ liệu.
- Mượn khả biến — dùng khi cần chỉnh sửa dữ liệu tại chỗ mà không cần tạo bản sao.
// Pattern 1: Take ownership, return a new value
fn process_and_return(mut input: String) -> String {
input.push_str(" -- processed");
input // ownership transfers to caller
}
// Pattern 2: Borrow for read-only inspection
fn contains_keyword(text: &str, keyword: &str) -> bool {
text.to_lowercase().contains(&keyword.to_lowercase())
}
// Pattern 3: Borrow mutably for in-place modification
fn sanitize(input: &mut String) {
*input = input.trim().to_string();
}
fn main() {
// Pattern 1
let raw = String::from("user input");
let processed = process_and_return(raw);
// raw is now invalid, processed owns the data
// Pattern 2
let found = contains_keyword(&processed, "input");
println!("Contains 'input': {}", found);
// Pattern 3
let mut padded = String::from(" spaces everywhere ");
sanitize(&mut padded);
println!("Sanitized: '{}'", padded);
}Việc lựa chọn đúng pattern không chỉ giúp code rõ ràng và hiệu quả hơn, mà còn thể hiện sự hiểu biết sâu về Rust — điều mà nhà tuyển dụng đánh giá cao.
Các lỗi phổ biến và cách khắc phục
Dưới đây là những lỗi mà cả lập trình viên mới lẫn người có kinh nghiệm đều gặp phải khi làm việc với ownership và borrowing:
fn main() {
// Error E0502: cannot borrow as mutable because also borrowed as immutable
let mut scores = vec![90, 85, 78];
// Fix: finish using the immutable borrow before mutating
let first = scores[0]; // copy (i32 is Copy), no active borrow
scores.push(95); // mutable borrow -- no conflict
println!("First: {}, All: {:?}", first, scores);
// Error E0382: use of moved value
let name = String::from("Alice");
let greeting = format!("Hello, {}", name); // format! borrows, doesn't move
println!("{} says {}", name, greeting); // both valid
// Error E0597: borrowed value does not live long enough
let outer;
{
let inner = String::from("temporary");
outer = inner; // move instead of borrow -- extends lifetime
}
println!("{}", outer); // works: outer owns the value
}Mỗi lỗi đều đi kèm mã lỗi cụ thể (E0502, E0382, E0597) giúp lập trình viên nhanh chóng tra cứu tài liệu chính thức của Rust. Trong phỏng vấn, việc trình bày được cách đọc và xử lý các thông báo lỗi của compiler là một điểm cộng đáng kể.
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Kết luận
Hệ thống Ownership và Borrowing là đặc trưng độc đáo nhất của Rust, tạo nên sự khác biệt cơ bản so với các ngôn ngữ khác. Việc nắm vững ba quy tắc ownership, hiểu rõ cơ chế borrowing với immutable và mutable references, cũng như biết cách sử dụng lifetime annotation sẽ giúp lập trình viên viết code an toàn và hiệu quả.
Đối với những ai đang chuẩn bị phỏng vấn kỹ thuật Rust, hãy tập trung vào:
- Giải thích được sự khác biệt giữa move, copy và clone.
- Trình bày quy tắc độc quyền của mutable reference.
- Đọc và xử lý các thông báo lỗi của Rust compiler một cách tự tin.
- Áp dụng đúng ownership pattern cho từng tình huống cụ thể.
Thực hành với các ví dụ code trong bài viết này và thử thay đổi chúng để quan sát cách compiler phản hồi. Đó là cách học Rust hiệu quả nhất — thông qua đối thoại với compiler.
Thẻ
Chia sẻ
Bài viết liên quan

Ownership và Borrowing trong Rust: Hướng Dẫn Toàn Diện
Làm chủ hệ thống ownership và borrowing của Rust. Quy tắc sở hữu, tham chiếu, lifetime và các mẫu quản lý bộ nhớ nâng cao.

Cau hoi phong van Rust: Huong dan day du 2026
25 cau hoi phong van Rust thuong gap nhat. Ownership, borrowing, lifetime, trait, async va concurrency voi cau tra loi chi tiet cung vi du code.

Rust: Kiến Thức Nền Tảng Dành Cho Lập Trình Viên Có Kinh Nghiệm Năm 2026
Tiếp cận Rust nhanh chóng dựa trên kiến thức lập trình hiện có. Ownership, borrowing, lifetimes và các pattern thiết yếu được giải thích dành cho lập trình viên đến từ C++, Java hoặc Python.