Mở đầu
Chào mừng các bạn đã quay trở lại với chuỗi bài viết về Rust! Ở bài viết trước, chúng ta đã hoàn thành việc thiết lập môi trường cho Rust. Hôm nay, chúng ta sẽ cùng khám phá những điều thú vị về biến (Variables) và hằng số (Constants) trong Rust, cùng xem chúng khác biệt như thế nào so với các ngôn ngữ lập trình khác.
Biến (Variables)
Biến trong Rust tương tự như các ngôn ngữ lập trình khác, giúp chúng ta lưu trữ các giá trị từ số, chữ cái cho đến các kiểu dữ liệu phức tạp hơn. Tuy nhiên, Rust có một số quy tắc đặc biệt khi sử dụng biến.
Mặc định là Immutable
Khi khởi tạo biến trong Rust, chúng ta sử dụng từ khóa let
. Mặc định, biến được khai báo sẽ là immutable (không thể thay đổi). Điều này có nghĩa là sau khi khai báo, bạn không thể thay đổi giá trị của biến đó.
rust
fn main() {
let name = "Quan";
println!("My name is: {name}");
name = "Quan Troy";
println!("My name is: {name}");
}
shell
// Ouput: Error message "cannot assign twice to immutable variable `name`"
// Bởi vì bạn đã cố gán giá trị cho `name` trong khi nó là Immutable
Trình biên dịch Rust sẽ đảm bảo rằng nếu bạn khai báo một giá trị là immutable, nó sẽ thực sự không thay đổi trong suốt thời gian thực thi, giúp bạn quản lý mã nguồn tốt hơn và tránh những lỗi không mong muốn do việc thay đổi giá trị biến mà bạn không hay biết.
Tuy nhiên, bạn có thể khai báo một biến là mutable (có thể thay đổi) bằng cách thêm mut
vào trước tên biến. Việc này không chỉ cho phép biến thay đổi, mà cũng giúp người đọc code nhận biết rằng biến này có thể thay đổi giá trị sau này. Hãy cùng sửa lại ví dụ trên một chút:
rust
fn main() {
let mut name = "Quan";
println!("My name is: {name}");
name = "Quan Troy";
println!("My name is: {name}");
}
shell
//Output
% cargo run
Compiling hello-world v0.1.0 (/<PATH>/hello-world)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.18s
Running `target/debug/hello-world`
My name is: Quan
My name is: Quan Troy
Ở đây, bạn đã thêm mut
để biến name
có thể thay đổi giá trị. Nhưng khi nào nên thêm mut
và khi nào không? Việc này yêu cầu bạn phải hiểu rõ về mã nguồn của mình để có thể quyết định sử dụng biến immutable hay mutable hiệu quả. Điều này sẽ giúp tránh việc khai báo không cần thiết trong trường hợp bạn không muốn biến của mình thay đổi.
Sự Ẩn Danh (Shadowing)
Rust cho phép bạn "biến hình" để biến có cùng tên xuất hiện hai lần, mà không gây ra xung đột. Biến sau sẽ làm "lu mờ" biến trước, và trình biên dịch chỉ nhận diện biến thứ hai.
rust
fn main() {
let name = "Quan";
let name = "Quan Troy";
println!("My name is: {name}");
}
shell
//Ouput: My name is Quan Troy
Để kiểm tra xem đây có phải là hai biến khác nhau hay không, bạn có thể thêm dòng code println!("Address of name: {:p}", &name);
bên dưới để in ra địa chỉ ô nhớ của name
được khai báo lần thứ nhất và thứ hai.
Mặc dù ví dụ trên có vẻ tương tự như việc sử dụng mut
, nhưng thực tế chúng hoàn toàn khác nhau. Biến được khai báo qua Shadowing vẫn là immutable (không thay đổi) khi được gán lại. Một lợi ích khác của Shadowing là bạn có thể tạo ra biến mới cùng tên nhưng khác kiểu dữ liệu, điều mà mut
không thực hiện được.
rust
let name = "Quan Troy";
let name = name.len(); // Không có vấn đề gì
rust
let name = "Quan Troy";
name = name.len(); // Lỗi không cho phép mutate thay đổi kiểu dữ liệu
Shadowing giúp bạn giữ cho mã nguồn gọn gàng hơn, tránh việc phải đặt tên biến dài dòng như name_str
và name_num
mà chỉ cần name
là đủ.
Kiểu Dữ Liệu
Rust rất thông minh và có thể tự động suy đoán loại dữ liệu của biến. Tuy nhiên, trong một số trường hợp, bạn sẽ cần chỉ rõ để Rust hiểu:
rust
let secret_number: u32 = 42; // u32 là số nguyên không âm 32-bit
Phạm Vi (Scope)
Biến trong Rust có phạm vi sống trong những "ngôi nhà" được gọi là scope, được định nghĩa trong dấu ngoặc nhọn {}
. Khi ra khỏi phạm vi này, biến sẽ bị xóa để giải phóng bộ nhớ:
rust
{
let ghost = "Quan!";
} // ghost biến mất ở đây!
Hằng Số (Constants)
Hằng số cũng là các giá trị không thay đổi giống như biến immutable, nhưng có một số khác biệt. Hằng số không thể sử dụng mut
, chúng không chỉ mặc định mà luôn luôn là immutable. Khi khai báo, bạn sử dụng từ khóa const
và phải chỉ định kiểu dữ liệu. Quy tắc đặt tên trong Rust cho hằng số là sử dụng tất cả chữ hoa và dấu gạch dưới giữa các từ.
rust
fn main() {
const MAX_POINT: u32 = 100_000;
println!("Max point: {}", MAX_POINT);
}
shell
//Output: Max point: 100000
Phạm Vi của Hằng Số
Hằng số có thể được khai báo ở phạm vi toàn cục hoặc cục bộ. Không thể khai báo cùng một hằng số hai lần trong cùng một phạm vi (scope), nhưng nếu ở các phạm vi khác nhau thì điều đó là hoàn toàn có thể.
rust
const MAX_POINT: u32 = 100; //global scope
//const MAX_POINT: u32 = 1000; -> ERROR
fn main() {
const MAX_POINT: u32 = 100_000; // OK -> chỉ có scope trong "{}" của hàm main
println!("Max point: {}", MAX_POINT);
}
shell
//Output: Max point: 100000
Tổng kết
Trong bài viết này, chúng ta đã xem xét những khái niệm cơ bản về biến trong Rust, bao gồm sự khác biệt giữa immutable và mutable, tính chất của hằng số (constants) và Shadowing. Việc quản lý biến trong Rust không chỉ đảm bảo an toàn mà còn nâng cao tính rõ ràng và hiệu quả của mã nguồn. Hy vọng bài viết này sẽ giúp bạn tự tin hơn khi làm việc với Rust. Nếu bạn có thắc mắc nào, đừng ngần ngại để lại câu hỏi dưới bài viết nhé! Chúc bạn trở thành một Rustacean thật giỏi!
Cảm ơn bạn đã dành thời gian đọc bài viết này.
Tài Liệu Tham Khảo
Hướng dẫn chính thức về Rust
source: viblo