· Phạm Thành Nam · Ngôn ngữ Rust · 4 phút đọc

30 Ngày học Rust - Day 2: Biến, Bất biến (Mutability) và Shadowing

Day 2: Tại sao mọi biến trong Rust mặc định là không thể thay đổi? Tìm hiểu khái niệm Mutability và kỹ thuật Shadowing độc đáo giúp code an toàn tự động.

Day 2: Tại sao mọi biến trong Rust mặc định là không thể thay đổi? Tìm hiểu khái niệm Mutability và kỹ thuật Shadowing độc đáo giúp code an toàn tự động.

Lật đổ thói quen cũ

Trong Python hay JS, nếu bạn tạo một biến x = 5, bạn có thể thản nhiên sửa nó thành x = 10 ở dòng kế tiếp. Bạn coi đó là một lợi ích. Nhưng trình biên dịch của Rust nhìn hành vi đó và gào lên: “RỦI RO QUÁ!”.

Rust thiết kế dựa trên một triết lý an toàn: Nếu bạn không nói, thì bạn không được chạm vào.

1. Mặc định là Bất biến (Immutable)

Hãy xem đoạn code phổ thông sau:

fn main() {
    let x = 5;
    println!("Giá trị của x: {}", x);
    x = 6; // Dòng này sẽ làm trình biên dịch VẢ BẠN NGAY LẬP TỨC
    println!("Giá trị mới của x: {}", x);
}

Nếu chạy cargo run, Rust sẽ hiện lỗi màu đỏ chói với thông báo cực kỳ lịch sự nhưng vô tình: cannot assign twice to immutable variable x.

Mọi biến tạo bằng từ khóa let trong Rust mặc định là khóa cứng (readonly/immutable). Tại sao lại cực đoan như vậy? Vì trong các hệ thống xử lý đa luồng (Multi-threading), nếu hai luồng cùng cố đè lên một biến vào cùng một tích tắc (Race Condition), hệ thống có nguy cơ sập hoàn toàn hoặc lấy sai dữ liệu ngân hàng. Sự cấm cản này ép bạn tư duy kỹ trước khi cho phép dữ liệu bị thay đổi.

2. Xin phép để thay đổi: Từ khóa mut

Vậy nếu mình muốn đếm số người truy cập (số thay đổi liên tục) thì sao? Rất đơn giản, hãy khai báo cho trình biên dịch biết ý định đó bằng từ khóa mut (viết tắt của Mutable):

fn main() {
    let mut x = 5; // Tôi tuyên bố biến này CÓ THỂ BỊ BIẾN ĐỔI
    console.log!("x ban đầu là: {}", x);
    x = 6; // Ok, thoải mái!
    console.log!("x bây giờ là: {}", x);
}

Nhờ từ khóa mut, bất kỳ lập trình viên nào vô tình đọc code của bạn cũng nhận ra ngay: “À, biến này đang bị biến đổi ngầm ở đâu đó bên dưới”. Nó tăng tính dễ đọc lên hàng nghìn lần.

3. Thuật phân thân: Kỹ thuật Shadowing

Rust cung cấp một kỹ thuật “Hack não” hơn cho việc gán biến gọi là Shadowing (Che phủ). Nó cho phép bạn gõ let lại y nguyên cái tên cũ!

fn main() {
    let spaces = "   ";           // Biến ban đầu kiểu chuỗi (String)
    let spaces = spaces.len();    // Biến MỚI đè nhầm tên cũ, kiểu số (Integer)
    
    println!("Số khoảng trắng: {}", spaces); // In ra: 3
}

Khoan! Mới ban nãy cấm đổi biến, sao giờ lại cho phép đổi cả kiểu dữ liệu cơ á?

Khác biệt là sự có mặt của từ khóa let lần thứ 2. Bạn hoàn toàn không sửa đổi cái biến cũ. Bạn đang tạo ra một cái biến mới tinh, cấp phát vùng nhớ mới, nhưng tận dụng lại tên gọi spaces. Biến spaces cũ biến mất đằng sau bóng tối (Bị Shadowed).

Câu hỏi thường gặp (FAQ)

Vậy let bất biến có giống với const hằng số không?

Chúng khác nhau!

  • const là Hằng số chân lý vĩnh cửu. Giá trị của const phải được tính toán xong ngay lúc Biên dịch (Cấm dùng kết quả từ hàm động trả về). Bạn buộc phải ghi rõ kiểu dữ liệu: const MAX_POINTS: u32 = 100_000;.
  • let (bất biến) thì chỉ là không cho đổi giá trị sau khi đã gán. Giá trị của nó có thể được lấy từ thao tác tính toán, đọc API, đọc file khi người dùng nhấp chạy chương trình.

Dùng Shadowing có làm tốn bộ nhớ hơn mut không?

Thực chất Shadowing là cấp phát biến mới, nên ở mức độ cực vi mô là tốn RAM hơn so với việc ghi đè trực tiếp của bộ nhớ. Nhưng Trình tối ưu (LLVM Compiler) của Rust đủ thông minh để tái cấu trúc vùng nhớ khi máy tự động compile. Đổi lại, Shadowing tiết kiệm cho bạn việc phải nghĩ cực nhọc để nặn ra cái tên biến spaces_strspaces_num.


Sẵn sàng cho Ngày 3? Giờ bạn đã kiểm soát được biến. Ngày mai, chúng ta bắt đầu dạy cho chương trình biết đưa ra quyết định thông minh thông qua luồng điều khiển trong [Day 3: Control Flow (if, match, loop)].

Chuỗi bài viết

30 Ngày Trở Thành Rustacean Tinh Hoa

Phần 2 / 30

30 bài viết
  1. Phần 1 30 Ngày học Rust - Day 1: Lời chào thế giới và Vũ khí Cargo
  2. Phần 2 30 Ngày học Rust - Day 2: Biến, Bất biến (Mutability) và Shadowing
  3. Phần 3 30 Ngày học Rust - Day 3: Luồng điều khiển (Control Flow) và sức mạnh của Match
  4. Phần 4 30 Ngày học Rust - Day 4: Ownership - Khái niệm định hình cỗ thần khí Rust
  5. Phần 5 30 Ngày học Rust - Day 5: Khóa học nhặt đồ chung với References & Borrowing
  6. Phần 6 30 Ngày học Rust - Day 6: Đắp nặn dự án với Structs và Methods
  7. Phần 7 30 Ngày học Rust - Day 7: Quyền năng vô song của Enums và Pattern Matching
  8. Phần 8 30 Ngày học Rust - Day 8: Quản lý dự án với Packages, Crates và Modules
  9. Phần 9 30 Ngày học Rust - Day 9: Các bộ sưu tập chung (Vectors, Strings, Hash Maps)
  10. Phần 10 30 Ngày học Rust - Day 10: Trị dứt điểm Ác mộng Lỗi (Error Handling)
  11. Phần 11 30 Ngày học Rust - Day 11: Generics - Ma thuật Đa hình kiểu dữ liệu
  12. Phần 12 30 Ngày học Rust - Day 12: Traits - Kẻ ban phát quyền năng và Lập pháp Hành vi
  13. Phần 13 30 Ngày học Rust - Day 13: Vượt qua Boss cuối 'Lifetimes' - Sinh Mệnh Biến
  14. Phần 14 30 Ngày học Rust - Day 14: Tận diệt Bug từ trứng nước bằng Automated Tests
  15. Phần 15 30 Ngày học Rust - Day 15: Thực chiến Build Command Line App (CLI) đầu đời
  16. Phần 16 30 Ngày học Rust - Day 16: Functional Magic chốn trần gian (Closures & Iterators)
  17. Phần 17 30 Ngày học Rust - Day 17: Quyền năng của Smart Pointers
  18. Phần 18 30 Ngày học Rust - Day 18: Chinh phục Da luồng (Concurrency) - Cú đâm quyết định của Rust
  19. Phần 19 30 Ngày học Rust - Day 19: Tranh cãi Lập trình hướng đối tượng (OOP) trong Rust
  20. Phần 20 30 Ngày học Rust - Day 20: Mổ xẻ nghệ thuật Advanced Pattern Matching
  21. Phần 21 30 Ngày học Rust - Day 21: Sức mạnh tối thượng của Advanced Traits
  22. Phần 22 30 Ngày học Rust - Day 22: Về Phe Bóng Tối với Unsafe Rust
  23. Phần 23 30 Ngày học Rust - Day 23: Quyền năng của Đấng Tạo Hóa (Declarative Macros)
  24. Phần 24 30 Ngày học Rust - Day 24: Siêu Năng Lực Procedural Macros (Macro Thủ Tục)
  25. Phần 25 30 Ngày học Rust - Day 25: Xây dựng TCP Web Server Đa Luồng (Phần 1: Lõi Socket)
  26. Phần 26 30 Ngày học Rust - Day 26: Xây dựng TCP Web Server Đa Luồng (Phần 2: Bắn trả File HTML)
  27. Phần 27 30 Ngày học Rust - Day 27: Xây dựng TCP Web Server Đa Luồng (Phần 3: The Thread Pool)
  28. Phần 28 30 Ngày học Rust - Day 28: Xây dựng TCP Web Server Đa Luồng (Phần 4: Graceful Shutdown)
  29. Phần 29 30 Ngày học Rust - Day 29: Lên đỉnh Web Server Thực Chiến với Axum và Tokio
  30. Phần 30 30 Ngày học Rust - Day 30: Vượt Vọng Vàng - Đúc Kết Hành Trình Rustacean Tinh Hoa

Bình luận

Quay lại Blog

Bài viết liên quan

Xem tất cả bài viết »