blog.dopana

Back

对于从 JavaScript 或 TypeScript 转到 Rust 的开发者来说,Rust 的语法表面上看相当亲切(大括号、let、闭包箭头函数等)。然而,在这层相似的外表下,两者底层的运行模型与内存架构有着天壤之别。

像给 10 岁孩子解释:租房公寓 vs 私人精密车间#

  • JavaScript (全托管租房公寓): 你直接搬进去住,水电随开随用,垃圾也可以顺手堆在走廊。后台有一位清洁阿姨(垃圾回收器 Garbage Collector)定期四处巡视清理。如果你找一个不存在的东西,系统会返回 undefinednull
  • Rust (自持精密机械车间): 每一把螺丝刀和扳手都有墙上的专属挂钩和唯一的法定持有者。任何借用与移动都必须严格记录。没有后台清洁阿姨——工具在离开房间的一毫秒内就会自动完成清理(RAII 编译期确定性销毁)。
graph TD
    subgraph JavaScript ["JavaScript 运行时模型"]
        JSEval["动态类型 & 对象模型"] --> JSHeap["V8 引擎堆内存"]
        JSHeap --> GC["后台垃圾回收器(定期暂停线程)"]
        JSEval --> JSNull["null / undefined(运行时抛出 TypeError)"]
    end
    subgraph Rust ["Rust 编译期模型"]
        RustEval["静态强类型系统"] --> RustCheck["所有权与借用检查器"]
        RustCheck --> Bin["零开销机器码(无 GC,RAII 确定性析构)"]
        RustEval --> RustEnum["Option<T> & Result<T, E>(编译期强制处理)"]
    end

1. 变量与可变性(Mutability)#

在 JavaScript 中,const 仅阻止重新绑定引用,对象内部属性依然可以随时篡改。而在 Rust 中,let 声明的变量默认是彻底不可变的(Deep Immutability)。

javascript.js
// JS: let 可重新赋值,const 仅防止重新绑定
let x = 10;
x = 20; // 正常

const user = { name: "Alice" };
user.name = "Bob"; // 正常!对象内部字段依然可变
javascript
rust.rs
// Rust: 默认不可变,修改必须显式加上 'mut'
let x = 10;
// x = 20; // ❌ 编译报错: cannot assign twice to immutable variable

let mut y = 10;
y = 20; // ✅ 正常

struct User { name: String }
let user = User { name: String::from("Alice") };
// user.name = String::from("Bob"); // ❌ 编译报错: user 未声明为 mut
rust

2. 函数与隐式返回值(Implicit Returns)#

在 Rust 中,代码块末尾不加分号 ; 的最后一个表达式会被自动当作函数的返回值。

javascript.js
// JavaScript
function add(a, b) {
    return a + b;
}
const multiply = (a, b) => a * b; // 箭头函数隐式返回
javascript
rust.rs
// Rust: 必须声明类型;省略末尾 ';' 即可隐式返回
fn add(a: i32, b: i32) -> i32 {
    a + b // 无分号 -> 自动 return
}

// 闭包(匿名函数)
let multiply = |a: i32, b: i32| a * b;
rust

3. 对象/类 vs 结构体/Trait#

JavaScript 使用基于原型/Class 的面向对象模型,并依赖 this。Rust 则将数据(struct)、行为(impl)和接口契约(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>#

JavaScript 中最常见的线上崩溃就是 TypeError: Cannot read properties of undefined。Rust 在编译期彻底消灭了 null,转而使用枚举 Option<T>

graph LR
    JSNullCheck["JS: user?.address?.city"] -->|运行时遇到 undefined| Crash["未捕获的 TypeError 导致应用崩溃"]
    RustOption["Rust: Option&lt;String&gt;"] -->|编译器强制 match 校验| SafeHandling["编译期提供 100% 安全保障"]
javascript.js
// JS: 可能返回 null 或 undefined
function findUser(id) {
    if (id === 1) return { name: "Alice" };
    return null;
}
const user = findUser(2);
// console.log(user.name); // ❌ 运行时报错: Cannot read properties of null
javascript

5. 错误处理:try/catch vs Result<T, E>#

JavaScript 使用异常抛出机制(throw),会跨越调用栈造成隐式跳转。Rust 将错误视为普通返回值,统一使用枚举 Result<T, E>

javascript.js
// JS: try / catch / throw
try {
    const data = JSON.parse(rawText);
    console.log(data);
} catch (err) {
    console.error("JSON 解析失败:", err.message);
}
javascript
rust.rs
// Rust: Result<T, E> 与 ? 错误传播运算符
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!("计算结果: {val}"),
        Err(e) => eprintln!("解析错误: {e}"),
    }
}
rust

核心特性快速对照表#

对比维度JavaScript / TypeScriptRust
类型系统动态类型 (JS) / 结构化静态 (TS)静态类型、名义类型 (Nominal)、强类型
内存管理垃圾回收器 (V8 GC)所有权机制 (Ownership) 与 RAII
缺失值表示nullundefinedOption<T> (SomeNone)
错误处理try / catch / throw 异常Result<T, E>? 运算符
异步模型单线程事件循环 (Promise)多线程异步运行时 (Tokio / Future)
包管理工具npm / bun / pnpm (package.json)cargo (Cargo.toml)
不变性保证const(仅限制浅层引用)let(默认深层绝对不可变)

总结#

  • Rust 语法对 JS/TS 开发者极为友好,但语义在编译期受到极其严格的法律级检验。
  • 不可变性是 Rust 的默认规则,修改必须显式标注 mut
  • 摒弃了面向对象继承,采用 struct 数据组合与 trait 接口实现。
  • OptionResult 全面替代了 null 与异常,将运行时错误前置为编译期提醒。

参考资料#