Chuyển đến nội dung chính
Mô-đun 3 · Bài học 1 trong tổng số 25/14 trong khóa học~15 min
Bài học theo mô-đun (1/2)

Quy tắc sở hữu

Quyền sở hữu là tính năng độc đáo nhất của Rust. Nó cho phép ngôn ngữ đảm bảo an toàn bộ nhớ mà không cần Bộ thu gom rác hoặc buộc bạn phải phân bổ bộ nhớ heap theo cách thủ công (như trong C hoặc C++).

Ba quy tắc sở hữu

Việc quản lý bộ nhớ trong Rust được điều chỉnh bởi ba quy tắc đơn giản được trình biên dịch kiểm tra nghiêm ngặt:

  1. Mỗi giá trị trong Rust có một biến được gọi là chủ sở hữu của nó.
  2. Mỗi lần chỉ có thể có một chủ sở hữu.
  3. Khi chủ sở hữu vượt quá phạm vi, giá trị sẽ tự động bị loại bỏ.

Ngăn xếp và đống

Để hiểu quyền sở hữu, điều quan trọng là phải biết dữ liệu nằm ở đâu:

  • Ngăn xếp: lưu trữ dữ liệu có kích thước cố định được biết tại thời điểm biên dịch (ví dụ: số nguyên, boolean). Truy cập cực kỳ nhanh chóng.
  • Heap: lưu trữ dữ liệu có kích thước động không xác định tại thời điểm biên dịch (ví dụ: String). Việc phân bổ chậm hơn và yêu cầu một con trỏ được lưu trữ trên Ngăn xếp.

Khái niệm “chuyển động”

Khi chúng ta gán một biến được cấp phát heap cho một biến khác, Rust di chuyển (hoặc chuyển) quyền sở hữu giá trị đó, làm mất hiệu lực biến đầu tiên:

Rust
let s1 = String::from("ciao");
let s2 = s1; // Ownership of the text moves to s2. s1 is NO longer usable!

// println!("{}", s1); // COMPILATION ERROR! s1 is "borrow of moved value"
println!("{}", s2); // Valid!

Hành vi này tránh được "lỗi trống kép" (cố gắng giải phóng cùng một bộ nhớ hai lần khi vượt quá phạm vi), vì chỉ s2 mới giải phóng bộ nhớ.

Khái niệm “Bản sao” (Bản sao sâu)

Nếu rõ ràng chúng ta cần sao chép toàn bộ nội dung của String (cả con trỏ ngăn xếp và dữ liệu văn bản thực tế trong vùng heap), chúng ta có thể sử dụng phương thức .clone(). Điều này sao chép hoàn toàn dữ liệu trên heap, giữ cho cả biến gốc và biến mới hợp lệ, với chi phí tăng thêm về hiệu năng cho việc phân bổ bộ nhớ mới:

Rust
let s1 = String::from("ciao");
let s2 = s1.clone(); // Deep copy. Both variables remain valid!
println!("s1: {}, s2: {}", s1, s2);

Khái niệm “Sao chép”

Đối với các loại đơn giản được lưu trữ hoàn toàn trên Ngăn xếp (như số nguyên i32, booleans bool, ký tự char), phép gán sẽ thực hiện bản sao nông tự động thực tế, nghĩa là cả hai biến vẫn hợp lệ:

Rust
let x = 5;
let y = x; // Copies the value 5 onto the stack. Both variables are usable!
println!("x: {}, y: {}", x, y); // Valid!

Hãy tự mình thử

tập thể dục#rust.m3.l1.e1
Nỗ lực: 0Đang tải…

Khai báo một Chuỗi s1 chứa văn bản 'hello'. Gán s1 cho s2 (để chuyển quyền sở hữu). Cuối cùng, in s2 bằng println!.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Sử dụng `let s2 = s1;` để chuyển quyền sở hữu sang `s2` và in nó bằng `println!('{}', s2);`.

Giải pháp khả dụng sau 3 lần thử

tập thể dục#rust.m3.l1.e2
Nỗ lực: 0Đang tải…

Khai báo một biến số nguyên x có giá trị là 5. Gán x cho y (thực hiện sao chép). Cuối cùng, in cả x và y trong cùng một println! tuyên bố.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Thực hiện `let y = x;` rồi in cả hai giá trị, ví dụ: `println!('{} {}', x, y);`.

Giải pháp khả dụng sau 3 lần thử

tập thể dục#rust.m3.l1.e3
Nỗ lực: 0Đang tải…

Declare a String s1 with the value "Rust". Create a deep copy of s1 in a new variable s2 using the clone method. Finally, print both s1 and s2 separated by a space using the println! macro.

Đang tải trình chỉnh sửa…
Hiển thị gợi ý

Use `let s2 = s1.clone();` to deep copy the value of `s1` into `s2`, then print them with `println!("{} {}", s1, s2);`.

Giải pháp khả dụng sau 3 lần thử