blog.dopana

Back

Đối với các lập trình viên chuyển từ JavaScript hoặc TypeScript sang Rust, cú pháp thoạt nhìn có vẻ khá quen thuộc (dấu ngoặc nhọn, từ khóa let, hàm mũi tên/closure). Tuy nhiên, bên dưới bề mặt là một mô hình thực thi và kiến trúc quản lý bộ nhớ hoàn toàn khác biệt.

Explain Like I’m 10: Căn Hộ Chung Cư Thuê vs Xưởng Cơ Khí Sở Hữu Riêng#

  • JavaScript (Căn hộ thuê trọn gói): Bạn dọn vào ở, dùng điện nước thoải mái và để rác ở hành lang. Định kỳ sẽ có bác lao công (Garbage Collector) đi dọn dẹp những thứ bạn vứt bừa bãi. Nếu bạn tìm một món đồ không có sẵn, bạn nhận về undefined hoặc null.
  • Rust (Xưởng cơ khí cá nhân chuẩn xác): Mỗi chiếc cờ-lê, kìm, ốc vít đều có móc treo riêng và người sở hữu duy nhất. Mọi hành động mượn hay di chuyển đồ đạc đều phải ghi chép rõ ràng. Không có lao công chạy nền nào cả—thay vào đó, từng món đồ tự động tiêu hủy đúng tích tắc nó rời khỏi căn phòng (cơ chế RAII kiểm soát tại thời điểm biên dịch).
graph TD
    subgraph JavaScript ["Mô Hình Runtime JavaScript"]
        JSEval["Dynamic Types & Objects"] --> JSHeap["V8 Heap Memory"]
        JSHeap --> GC["Garbage Collector chạy nền (Dừng luồng định kỳ)"]
        JSEval --> JSNull["null / undefined (Runtime TypeError)"]
    end
    subgraph Rust ["Mô Hình Compile-Time Rust"]
        RustEval["Static Strict Typing"] --> RustCheck["Borrow Checker & Type System"]
        RustCheck --> Bin["Mã máy tối ưu (Zero-cost, Không GC, Tự hủy RAII)"]
        RustEval --> RustEnum["Option<T> & Result<T, E> (Bắt buộc xử lý khi biên dịch)"]
    end

1. Biến Và Tính Bất Biến (Mutability)#

Trong JavaScript, const chỉ ngăn chặn việc gán lại biến, nhưng thuộc tính bên trong object vẫn có thể bị thay đổi tự do. Trong Rust, biến khai báo với let mặc định là bất biến hoàn toàn (deeply immutable).

javascript.js
// JS: let có thể gán lại, const chỉ chặn gán lại tham chiếu
let x = 10;
x = 20; // Hợp lệ

const user = { name: "Alice" };
user.name = "Bob"; // Vẫn thay đổi được nội dung object!
javascript
rust.rs
// Rust: Bất biến mặc định, muốn thay đổi phải dùng 'mut'
let x = 10;
// x = 20; // ❌ Lỗi: cannot assign twice to immutable variable

let mut y = 10;
y = 20; // ✅ Hợp lệ

struct User { name: String }
let user = User { name: String::from("Alice") };
// user.name = String::from("Bob"); // ❌ Lỗi: user không được khai báo mut
rust

2. Hàm Và Giá Trị Trả Về Ngầm Định (Implicit Returns)#

Trong Rust, biểu thức cuối cùng của khối code không có dấu chấm phẩy ; sẽ tự động được coi là giá trị trả về của hàm.

javascript.js
// JavaScript
function add(a, b) {
    return a + b;
}
const multiply = (a, b) => a * b; // Arrow function return ngầm định
javascript
rust.rs
// Rust: Bắt buộc định nghĩa kiểu dữ liệu; bỏ dấu ';' để return ngầm định
fn add(a: i32, b: i32) -> i32 {
    a + b // Không có dấu chấm phẩy -> tự động return
}

// Closure (Hàm ẩn danh)
let multiply = |a: i32, b: i32| a * b;
rust

3. Objects & Classes vs Structs & Traits#

JavaScript sử dụng mô hình Hướng đối tượng dạng Prototype/Class kèm theo con trỏ this. Rust tách biệt dữ liệu (struct) với hành vi (impl) và giao diện (trait).

javascript.js
// JavaScript Class
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }
    area() {
        return this.width * this.height;
    }
}
const rect = new Rectangle(10, 20);
console.log(rect.area());
javascript

4. null & undefined vs Option<T>#

Các ứng dụng JavaScript thường xuyên gặp lỗi runtime phổ biến nhất: TypeError: Cannot read properties of undefined. Rust loại bỏ hoàn toàn null ngay tại thời điểm biên dịch thông qua kiểu Option<T>.

graph LR
    JSNullCheck["JS: user?.address?.city"] -->|Gặp undefined khi chạy| Crash["Lỗi Runtime TypeError nếu quên check"]
    RustOption["Rust: Option&lt;String&gt;"] -->|Trình biên dịch ép xử lý None| SafeHandling["An toàn 100% khi biên dịch"]
javascript.js
// JS: Có thể trả về null hoặc undefined
function findUser(id) {
    if (id === 1) return { name: "Alice" };
    return null;
}
const user = findUser(2);
// console.log(user.name); // ❌ Lỗi sập app: Cannot read properties of null
javascript

5. Xử Lý Lỗi: try/catch vs Result<T, E>#

JavaScript sử dụng cơ chế ném ngoại lệ (throw) có thể nhảy cóc qua nhiều tầng stack mà không báo trước. Rust xem lỗi như một giá trị trả về bình thường thông qua enum Result<T, E>.

javascript.js
// JS: try / catch / throw
try {
    const data = JSON.parse(rawText);
    console.log(data);
} catch (err) {
    console.error("Lỗi parse JSON:", err.message);
}
javascript
rust.rs
// Rust: Result<T, E> kết hợp toán tử ?
fn parse_number(s: &str) -> Result<i32, std::num::ParseIntError> {
    let num = s.trim().parse::<i32>()?;
    Ok(num * 2)
}

fn main() {
    match parse_number("42") {
        Ok(val) => println!("Kết quả: {val}"),
        Err(e) => eprintln!("Lỗi parse: {e}"),
    }
}
rust

Bảng So Sánh Tổng Quan Nhanh#

Tiêu chíJavaScript / TypeScriptRust
Hệ thống kiểuĐộng (JS) / Cấu trúc tĩnh (TS)Tĩnh, Danh định (Nominal), Kiểu mạnh
Quản lý bộ nhớGarbage Collector (V8)Quyền sở hữu (Ownership) & Mượn (Borrowing)
Giá trị thiếunullundefinedOption<T> (Some hoặc None)
Xử lý lỗitry / catch / throwResult<T, E> cùng toán tử ?
Mô hình AsyncĐơn luồng Event Loop (Promise)Đa luồng Async Runtime (Tokio / Future)
Quản lý góinpm / bun / pnpm (package.json)cargo (Cargo.toml)
Tính bất biếnconst (chỉ nông bên ngoài)let (bất biến sâu mặc định)

Tổng kết#

  • Cú pháp Rust mang lại cảm giác thân quen cho lập trình viên JS/TS, nhưng hành vi ngữ nghĩa được kiểm soát chặt chẽ ở giai đoạn biên dịch.
  • Bất biến là mặc định trong Rust; cần ghi rõ mut khi muốn thay đổi.
  • Không có Class kế thừa; thay vào đó là sự kết hợp giữa structtrait.
  • OptionResult thay thế hoàn toàn cho null, undefinedthrow, biến lỗi runtime thành lỗi compile-time.

Tài liệu tham khảo#