# 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. - Published: 2026-03-31 - Updated: 2026-03-31 - Author: Anthony Fillion-Maillet - Tags: rust, ownership, borrowing, memory-management, interview - Reading time: 8 min --- 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. > **Mẹo 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: 1. Mỗi giá trị trong Rust có một owner duy nhất. 2. Tại mỗi thời điểm, chỉ có một owner. 3. 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. ```rust // move_semantics.rs 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). ```rust // copy_vs_move.rs 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 } ``` > **Lưu ý về hiệu năng** > > 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. ```rust // immutable_borrowing.rs 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. ```rust // mutable_borrowing.rs 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. ```rust // exclusivity_rule.rs 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 đó. ## 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 ```rust // lifetime_annotation.rs // '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 đó. ```rust // struct_lifetime.rs 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()); } ``` > **Bẫy thường gặp trong phỏng vấn** > > 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: 1. **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ả. 2. **Mượn bất biến** — lý tưởng cho các hàm chỉ đọc, kiểm tra dữ liệu. 3. **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. ```rust // ownership_patterns.rs // 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: ```rust // common_fixes.rs 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ể. ## 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. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/rust/rust-ownership-borrowing-demystified