Bỏ qua để đến nội dung

Biến trong Rust

Một biến (variable) đơn giản là một cái tên bạn đặt cho một giá trị để dùng lại sau này. Hãy hình dung nó như một chiếc hộp có dán nhãn: bạn bỏ một thứ vào hộp, viết tên lên nhãn, rồi gọi nó bằng cái tên đó thay vì lặp lại giá trị ở khắp nơi.

Trong Rust, bạn tạo biến bằng từ khóa let:

fn main() {
let age = 30;
println!("Tôi năm nay {age} tuổi");
}

Kết quả:

Tôi năm nay 30 tuổi
▶ Tự chạy thử

Hãy tách dòng đó ra:

  • let — nói với Rust “tôi đang tạo một biến mới.”
  • age — cái tên chúng ta chọn.
  • = — gán giá trị bên phải cho cái tên bên trái.
  • 30 — giá trị chúng ta lưu.

{age} bên trong println! in ra giá trị của biến. Cách này gọi là nội suy chuỗi (string interpolation) — Rust thay {age} bằng bất cứ giá trị nào age đang giữ.

Mọi giá trị trong Rust đều có một kiểu (type): số nguyên, văn bản, true/false, v.v. Có thể bạn nghĩ phải khai báo kiểu đó, nhưng thường thì không cần — Rust nhìn vào giá trị và tự suy ra. Điều này gọi là suy luận kiểu (type inference).

fn main() {
let score = 95; // Rust suy ra đây là số nguyên
let name = "Ferris"; // Rust suy ra đây là văn bản
let is_active = true; // Rust suy ra đây là boolean
println!("{name}: {score}, đang hoạt động = {is_active}");
}

Kết quả:

Ferris: 95, đang hoạt động = true
▶ Tự chạy thử

Nếu muốn viết rõ ràng, bạn có thể ghi kiểu sau dấu hai chấm. Cách này gọi là chú thích kiểu (type annotation):

let score: i32 = 95; // i32 = số nguyên 32 bit

Đây là phần khiến những ai đến từ ngôn ngữ khác bất ngờ: trong Rust, một khi đã gán giá trị cho biến, mặc định bạn không thể thay đổi nó. “Bất biến” (immutable) đơn giản là không thể thay đổi.

Đoạn code này sẽ không biên dịch được:

fn main() {
let count = 1;
count = 2; // ❌ lỗi: không thể gán hai lần cho biến bất biến
println!("{count}");
}

Trình biên dịch chặn bạn lại với một thông báo rõ ràng (bằng tiếng Anh):

error[E0384]: cannot assign twice to immutable variable `count`

Thông báo này nghĩa là “không thể gán hai lần cho biến bất biến count”. Vì sao một ngôn ngữ lại cố tình làm vậy? Vì rất nhiều lỗi đến từ việc giá trị thay đổi khi bạn không ngờ tới. Bằng cách khiến mọi thứ không thể thay đổi trừ khi bạn yêu cầu, Rust giúp code của bạn dễ đoán hơn. Đó là một tính năng, không phải một hạn chế.

Khi bạn thực sự cần một giá trị thay đổi, hãy thêm từ khóa mut (viết tắt của mutable = khả biến, có thể thay đổi):

fn main() {
let mut count = 1;
println!("Trước: {count}");
count = 2; // ✅ giờ thì được phép, nhờ có `mut`
println!("Sau: {count}");
}

Kết quả:

Trước: 1
Sau: 2
▶ Tự chạy thử

Rust cho phép bạn khai báo một biến mới trùng tên với biến cũ. Điều này gọi là shadowing (che khuất) — biến mới “phủ bóng” lên biến cũ:

fn main() {
let x = 5;
let x = x + 1; // một x mới, dựa trên x cũ -> 6
let x = x * 2; // lại một x mới nữa -> 12
println!("x bằng {x}");
}

Kết quả:

x bằng 12
▶ Tự chạy thử

Trông thì giống mut, nhưng thực ra khác:

  • mut thay đổi giá trị của cùng một biến.
  • Shadowing tạo ra một biến hoàn toàn mới chỉ là dùng lại cái tên — và nó thậm chí có thể có kiểu khác.

Shadowing rất tiện khi bạn muốn biến đổi một giá trị nhưng vẫn giữ nguyên tên:

fn main() {
let spaces = " "; // văn bản
let spaces = spaces.len(); // giờ là một con số (có bao nhiêu dấu cách)
println!("Số dấu cách: {spaces}");
}

Kết quả:

Số dấu cách: 3
▶ Tự chạy thử

Một hằng số (constant) là một giá trị không bao giờ thay đổi và được cố định ngay lúc biên dịch. Bạn khai báo nó bằng const thay vì let, và bạn bắt buộc phải ghi kiểu:

const MAX_SCORE: u32 = 100;
fn main() {
println!("Điểm tối đa là {MAX_SCORE}");
}

Kết quả:

Điểm tối đa là 100
▶ Tự chạy thử

Theo quy ước, hằng số được viết bằng UPPER_SNAKE_CASE. Đây là cách chúng so với biến thông thường:

Biến let Hằng số const
Có thể thay đổi? Chỉ khi có mut Không bao giờ
Bắt buộc ghi kiểu? Không bắt buộc (được suy luận) Bắt buộc
Kiểu đặt tên snake_case UPPER_SNAKE_CASE
Được đặt ở đâu Bên trong hàm Bất cứ đâu, kể cả bên ngoài hàm

Hãy dùng hằng số cho những giá trị thực sự cố định — như số giây trong một phút, hay một giới hạn tối đa.

  • Dùng snake_case — các từ viết thường nối với nhau bằng dấu gạch dưới: user_name, total_price.
  • Tên có thể chứa chữ cái, chữ số và dấu gạch dưới, nhưng không được bắt đầu bằng chữ số.
  • Chọn tên có ý nghĩa: age tốt hơn a.

Tạo một biến level bắt đầu từ 1. In nó ra, tăng nó lên 1 hai lần, và in sau mỗi lần thay đổi, để bạn có Cấp 1, Cấp 2, Cấp 3. Gợi ý: một giá trị thay đổi cần có mut, và level = level + 1; cộng thêm một.

Xem lời giải
fn main() {
let mut level = 1;
println!("Cấp {level}");
level = level + 1;
println!("Cấp {level}");
level = level + 1;
println!("Cấp {level}");
}

Kết quả:

Cấp 1
Cấp 2
Cấp 3
▶ Tự chạy thử

Khai báo một hằng số SECONDS_PER_MINUTE bằng 60. Bắt đầu với let time = 5; (tính bằng phút), rồi shadow time để nó giữ cùng khoảng thời gian đó nhưng tính bằng giây, và in ra Tức là 300 giây. Gợi ý: time mới có thể được tạo từ time cũ: let time = time * ...;.

Xem lời giải
const SECONDS_PER_MINUTE: u32 = 60;
fn main() {
let time = 5; // phút
let time = time * SECONDS_PER_MINUTE; // một `time` mới, giờ tính bằng giây
println!("Tức là {time} giây");
}

Kết quả:

Tức là 300 giây
▶ Tự chạy thử

Không cần mut: let thứ hai tạo ra một biến hoàn toàn mới dùng lại cái tên cũ. Rust còn suy ra time ở đây là u32, vì nó được nhân với hằng số kiểu u32.

  • Tạo biến bằng let; Rust thường tự suy luận kiểu cho bạn.
  • Mặc định biến là bất biến — thêm mut để cho phép thay đổi.
  • Shadowing khai báo lại một tên bằng let, tạo ra một biến mới (có thể đổi kiểu).
  • Hằng số (const) không bao giờ thay đổi, luôn cần ghi kiểu, và dùng UPPER_SNAKE_CASE.

Kiểm tra nhanh

1. Mặc định, bạn có thể gán lại giá trị cho một biến let trong Rust không?

2. Shadowing trong Rust là gì?

3. Theo trang này, biến trong Rust dùng quy ước đặt tên nào?

Điểm: 0 / 3