A reference allows you to refer to a value without taking ownership of it. When you have a reference to a value, that value will not be dropped when the reference goes out of scope. The & syntax creates a reference.
Tham chiếu (reference) cho phép bạn tham chiếu đến một giá trị mà không lấy quyền sở hữu của nó. Khi bạn có tham chiếu đến một giá trị, giá trị đó sẽ không bị drop khi tham chiếu ra khỏi phạm vi. Cú pháp & tạo một tham chiếu.
1fn main() {2 let s1 = String::from("hello");34 // &s1 creates a reference — does NOT take ownership5 let len = calculate_length(&s1);67 // s1 is still valid here because we only lent it8 println!("The length of '{s1}' is {len}.");910 // Dereferencing with * to get the value behind a reference11 let x = 5;12 let y = &x; // y is a reference to xThe action of creating a reference is called borrowing. Just as in real life, when you borrow something from someone, you give it back when you are done — you do not own it.
Hành động tạo tham chiếu được gọi là mượn (borrowing). Giống như trong cuộc sống thực, khi bạn mượn thứ gì đó từ người khác, bạn trả lại khi xong — bạn không sở hữu nó.
Rust's borrow checker enforces two core rules at compile time to guarantee memory safety. There is zero runtime cost for these checks.
Borrow checker của Rust thực thi hai quy tắc cốt lõi tại thời điểm biên dịch để đảm bảo an toàn bộ nhớ. Không có chi phí runtime nào cho những kiểm tra này.
- At any given time, you can have EITHER one mutable reference (&mut T), OR any number of immutable references (&T) — but not both at the same time.
Tại bất kỳ thời điểm nào, bạn có thể có HOẶC là một tham chiếu có thể thay đổi (&mut T), HOẶC là bất kỳ số lượng tham chiếu bất biến nào (&T) — nhưng không phải cả hai cùng lúc.
- References must always be valid — no dangling references allowed.
Tham chiếu phải luôn hợp lệ — không có tham chiếu treo (dangling references).
Hai quy tắc này được kiểm tra hoàn toàn tại thời điểm biên dịch. Nếu code của bạn biên dịch thành công, nó được đảm bảo không có data races, dangling pointers, hay use-after-free bugs.
Immutable references do not allow modifying the data. To modify data through a reference, use &mut. However, only one mutable reference to a specific piece of data can exist at a time — this is how Rust prevents data races.
Tham chiếu bất biến không cho phép thay đổi dữ liệu. Để thay đổi dữ liệu qua tham chiếu, dùng &mut. Tuy nhiên, chỉ có thể có một tham chiếu có thể thay đổi tới một dữ liệu cụ thể tại một thời điểm — đây là cách Rust ngăn chặn data races.
1fn main() {2 let mut s = String::from("hello");34 change(&mut s);56 println!("{s}"); // "hello, world"7}89fn change(some_string: &mut String) {10 some_string.push_str(", world");11}1fn main() {2 let mut s = String::from("hello");34 let r1 = &mut s;5 // let r2 = &mut s; // ERROR: cannot borrow `s` as mutable more than once67 println!("{r1}"); // r1 last used here89 // After r1 is last used, a new mutable reference is allowed10 let r2 = &mut s;11 r2.push_str(" world");12 println!("{r2}");13}Giới hạn chỉ một tham chiếu mutable tại một thời điểm ngăn chặn data races tại thời điểm biên dịch. Data race xảy ra khi: hai hoặc nhiều con trỏ cùng truy cập một dữ liệu, ít nhất một đang ghi, và không có cơ chế đồng bộ hóa. Rust loại bỏ hoàn toàn khả năng này.
Multiple immutable references can coexist, but you cannot combine immutable and mutable references. The scope of a reference ends at its last point of use, not at the closing curly brace (NLL — Non-Lexical Lifetimes).
Nhiều tham chiếu bất biến có thể tồn tại cùng lúc, nhưng không thể kết hợp tham chiếu bất biến và tham chiếu có thể thay đổi. Phạm vi của tham chiếu kết thúc tại điểm sử dụng cuối cùng, không phải khi dấu ngoặc nhọn đóng (NLL — Non-Lexical Lifetimes).
1fn main() {2 let mut s = String::from("hello");34 // Multiple immutable references are fine5 let r1 = &s;6 let r2 = &s;7 println!("{r1} and {r2}");8 // r1 and r2 are no longer used after this point (NLL)910 // Now a mutable reference is allowed11 let r3 = &mut s;12 r3.push_str(" world");13 println!("{r3}");1415 // This combination would be an ERROR:16 // let r1 = &s;17 // let r3 = &mut s; // ERROR: cannot borrow as mutable18 // // because already borrowed as immutable19 // println!("{r1} {r3}"); // if r1 is still in scope here20}A dangling reference occurs when you have a pointer to memory that has been freed. In C/C++, this is a common and dangerous bug. Rust prevents this at compile time: the compiler ensures data will not go out of scope before a reference to it does.
Tham chiếu treo (dangling reference) xảy ra khi bạn có con trỏ đến vùng bộ nhớ đã được giải phóng. Trong C/C++, đây là lỗi phổ biến và nguy hiểm. Rust ngăn điều này tại thời điểm biên dịch: trình biên dịch đảm bảo dữ liệu sẽ không ra khỏi phạm vi trước tham chiếu đến nó.
1// This would cause a compile ERROR — Rust prevents dangling references:2//3// fn dangle() -> &String { // returns a reference to a String4// let s = String::from("hello"); // s is created inside this function5// &s // ERROR: we return a reference to s6// } // but s is dropped here — dangling!7//8// error[E0106]: missing lifetime specifier910// The correct solution: return the String itself (transfer ownership)11fn no_dangle() -> String {12 let s = String::from("hello");13 s // ownership is transferred to the caller — no reference needed14}1516fn main() {17 let s = no_dangle();18 println!("{s}");19}Trình biên dịch Rust sẽ từ chối mọi code tạo ra dangling reference và đưa ra thông báo lỗi rõ ràng. Đây là một trong những tính năng an toàn mạnh mẽ nhất của Rust — loại bỏ hoàn toàn một lớp lỗi nguy hiểm.
A string slice is a reference to part of a String. The syntax uses range notation [start..end] where start is inclusive and end is exclusive. The type of a string slice is &str.
Lát cắt chuỗi (string slice) là tham chiếu đến một phần của String. Cú pháp dùng dải ký hiệu [start..end] trong đó start bao gồm và end không bao gồm. String slice là kiểu &str.
1fn main() {2 let s = String::from("hello world");34 let hello = &s[0..5]; // "hello" — bytes 0 through 45 let world = &s[6..11]; // "world" — bytes 6 through 1067 // Range shorthand8 let hello2 = &s[..5]; // same as [0..5]9 let world2 = &s[6..]; // same as [6..11]10 let whole = &s[..]; // the entire string as a slice1112 println!("{hello} {world}");Kiểu &str được khuyến nghị cho tham số hàm khi làm việc với chuỗi. Nó linh hoạt hơn &String vì chấp nhận cả tham chiếu đến String (tự động coerce) lẫn string literals.
Similar to string slices, you can create slices of arrays and vectors. The type of an array slice is &[T] where T is the element type. A slice is a reference to a contiguous sequence of memory, consisting of a pointer to the first element and a length.
Tương tự string slice, bạn có thể tạo slice của mảng và vector. Kiểu của slice mảng là &[T] trong đó T là kiểu phần tử. Slice là tham chiếu đến một chuỗi liên tiếp của bộ nhớ, gồm con trỏ đến phần tử đầu và độ dài.
1fn main() {2 let a = [1, 2, 3, 4, 5];34 // Array slice: type is &[i32]5 let slice = &a[1..3]; // [2, 3]6 println!("{:?}", slice); // [2, 3]78 println!("Sum of slice: {}", sum(slice));9 println!("Sum of all: {}", sum(&a)); // coerce [i32; 5] to &[i32]1011 // Vec slice works the same way12 let v = vec![10, 20, 30, 40, 50];Slices are non-owning types that store a reference to a contiguous sequence of elements in a collection. Each slice on the stack has two components: a pointer to the first element and a length. Slices are one of Rust's zero-cost abstractions.
Slice là kiểu không sở hữu (non-owning) lưu trữ tham chiếu đến một chuỗi liên tiếp các phần tử trong collection. Mỗi slice trên stack gồm hai thành phần: con trỏ đến phần tử đầu tiên và độ dài. Slice là một trong những zero-cost abstraction của Rust.
1fn main() {2 // String types3 let owned: String = String::from("hello world");4 let str_slice: &str = &owned[..5]; // borrows part of the String5 let literal: &str = "hello"; // a string literal is &str67 // Array and Vec slices8 let arr: [i32; 5] = [1, 2, 3, 4, 5];9 let arr_slice: &[i32] = &arr[1..3]; // borrows part of the array1011 let vec: Vec<i32> = vec![1, 2, 3, 4, 5];12 let vec_slice: &[i32] = &vec[..]; // borrows the entire Vec1314 // Slice internals: (pointer, length) — just 2 words on the stack15 println!("str_slice: '{}' len={}", str_slice, str_slice.len());16 println!("literal: '{}' len={}", literal, literal.len());17 println!("arr_slice: {:?} len={}", arr_slice, arr_slice.len());18 println!("vec_slice: {:?} len={}", vec_slice, vec_slice.len());19}Slice là zero-cost abstraction: chúng cung cấp API an toàn và tiện lợi mà không có bất kỳ chi phí runtime nào so với làm việc trực tiếp với con trỏ và độ dài. Trình biên dịch tối ưu hóa slice thành các thao tác con trỏ đơn giản.
Key Takeaways
Điểm Chính
- References let you use values without taking ownershipTham chiếu cho phép bạn sử dụng giá trị mà không lấy quyền sở hữu
- You can have many immutable references or one mutable referenceBạn có thể có nhiều tham chiếu bất biến hoặc một tham chiếu có thể thay đổi
- The borrow checker prevents dangling references at compile timeBorrow checker ngăn tham chiếu treo tại thời điểm biên dịch
- Slices are references to contiguous sequences in collectionsSlice là tham chiếu đến các chuỗi liên tiếp trong collection
Practice
Test your understanding of this chapter
How many mutable references (&mut T) to the same data can exist at the same time?
Có bao nhiêu tham chiếu có thể thay đổi (&mut T) đến cùng một dữ liệu có thể tồn tại cùng lúc?
You can have multiple immutable references (&T) to the same data at the same time.
Bạn có thể có nhiều tham chiếu bất biến (&T) đến cùng một dữ liệu cùng lúc.
Borrow without taking ownership
Mượn mà không lấy quyền sở hữu
fn print_length(s: String) { println!("length: {}", s.len()); } fn main() { let s = String::from("hello"); print_length(s); println!("{s}"); // s still valid }
What is the type of a string slice in Rust?
Kiểu dữ liệu của một lát cắt chuỗi (string slice) trong Rust là gì?
Rust prevents dangling references at runtime by using a garbage collector to track live references.
Rust ngăn tham chiếu treo (dangling reference) lúc runtime bằng cách dùng garbage collector để theo dõi tham chiếu còn sống.