28. tháng 2 2025
Gần đây tôi đang đọc cuốn "Hướng dẫn chính thức về Rust" và thấy rằng nó khá phức tạp. Một số khái niệm bên trong cuốn sách này khác biệt rất lớn so với những gì tôi đã làm trước đây. So sánh với các ngôn ngữ máy ảo có GC, hoặc giống như C và C++ – nơi mà bộ nhớ cần được giải phóng một cách chủ động, Rust lại có những đặc điểm độc đáo của riêng mình. Chủ yếu có ba quy bảng xếp hạng ngoại hạng anh 2025 mới nhất tắc cơ bản:
Ngoài ra, cần lưu ý rằng:
Bây giờ chúng ta hãy xem xét một ví bắn cá ăn xu online dụ cụ thể:
let x = 5;
let y = x;
Trong trường hợp này, thông thường hành vi có thể được hiểu là hai biến x
và y
sẽ trỏ tới cùng một vùng bộ nhớ chứa giá trị 5
. Sau đó, nếu xảy ra thay đổi, quá trình copy-on-write sẽ được thực hiện để tạo ra một bản sao thật sự. Tuy nhiên, do tính chất tiện lợi trong xử lý bộ nhớ đối với các kiểu dữ liệu đơn giản, Rust chọn cách sao chép trực tiếp giá trị thay vì sử dụng tham chiếu. Điều này áp dụng chủ yếu cho các kiểu dữ liệu nguyên thủy như số nguyên.
Tuy nhiên, đối với các kiểu dữ liệu phức tạp hơn, mọi thứ hoạt động khác biệt. Ví dụ:
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
Trong đoạn mã trên, nếu bạn chạy chương trình, nó sẽ báo lỗi vì s1
không còn hợp lệ sau khi gán giá trị cho s2
. Lý do là bởi vì trong Rust, việc gán let y = x
thực chất là di chuyển (move) giá trị từ x
sang y
, khiến x
không còn tồn tại nữa. Điều này giúp tránh tình trạng giải phóng bộ nhớ hai lần khi cả x
và y
rời khỏi phạm vi.
Nếu muốn sao chép giá trị thay vì di chuyển, bạn có thể sử dụng phương pháp clone
:
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
Điều này sẽ đảm bảo rằng cả s1
và s2
đều có các bản sao độc lập của chuỗi "hello".
Bạn có thể tự hỏi tại sao hành vi giữa x, y
và s1, s2
lại khác nhau. Thực tế, điều này phụ thuộc vào cách phân bổ bộ nhớ của các kiểu dữ liệu. Đối với các kiểu dữ liệu cơ bản như số nguyên (x, y
), kích thước của chúng có thể được xác định trước trong quá trình biên dịch, vì vậy chúng có thể được đặt trực tiếp trên ngăn xếp (stack). Ngược lại, đối với các kiểu dữ liệu phức tạp như String
hay cấu trúc dữ liệu khác, kích thước của chúng không thể được biết trước trong lúc biên dịch, do đó chúng cần được phân bổ trên đống (heap).