So sánh cú pháp và tư duy giữa Rust và JavaScript
So sánh chi tiết Rust và JavaScript/TypeScript: biến, tính bất biến (mutability), hàm, xử lý lỗi, Struct vs Class và quản lý bộ nhớ.
Đố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ề
undefinedhoặcnull. - 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).
// 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: 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 mutrust2. 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
function add(a, b) {
return a + b;
}
const multiply = (a, b) => a * b; // Arrow function return ngầm địnhjavascript// 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;rust3. 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 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// Rust: Struct (Dữ liệu) + Khối Impl (Hành vi)
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// Associated function (Constructor theo quy ước)
fn new(width: u32, height: u32) -> Self {
Rectangle { width, height }
}
// Method nhận tham chiếu &self
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect = Rectangle::new(10, 20);
println!("Diện tích: {}", rect.area());
}rust4. 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<String>"] -->|Trình biên dịch ép xử lý None| SafeHandling["An toàn 100% khi biên dịch"]
// 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 nulljavascript// Rust: Bắt buộc trả về Option<User>
struct User { name: String }
fn find_user(id: u32) -> Option<User> {
if id == 1 {
Some(User { name: String::from("Alice") })
} else {
None
}
}
fn main() {
let user = find_user(2);
// Compiler bắt buộc phải phân nhánh match xử lý cả trường hợp None!
match user {
Some(u) => println!("Tên user: {}", u.name),
None => println!("Không tìm thấy user"),
}
}rust5. 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>.
// 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: 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}"),
}
}rustBảng So Sánh Tổng Quan Nhanh#
| Tiêu chí | JavaScript / TypeScript | Rust |
|---|---|---|
| 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ếu | null và undefined | Option<T> (Some hoặc None) |
| Xử lý lỗi | try / catch / throw | Result<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ói | npm / bun / pnpm (package.json) | cargo (Cargo.toml) |
| Tính bất biến | const (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õ
mutkhi muốn thay đổi. - Không có Class kế thừa; thay vào đó là sự kết hợp giữa
structvàtrait. OptionvàResultthay thế hoàn toàn chonull,undefinedvàthrow, biến lỗi runtime thành lỗi compile-time.