Rust 快速入门 (从 JavaScript 开发者视角)-Day09

参考资料:

Rust官网

菜鸟教程-Rust教程
Rust一门赋予每个人构建可靠且高效软件能力的语言。

一、环境搭建与项目结构

安装工具链

JavaScript Rust
nvm 管理 Node 版本 rustup 管理 Rust 版本
npm / yarn / pnpm 包管理 cargo 包管理兼构建工具
package.json 项目配置 Cargo.toml 项目配置
bash 复制代码
# 安装 Rust(类似 nvm install)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 验证安装
rustc --version   # 编译器
cargo --version   # 包管理器

创建新项目

bash 复制代码
# JavaScript
npm init -y

# Rust
cargo new my-project
cd my-project && cargo run

Rust 项目结构:

复制代码
my-project/
├── Cargo.toml      # 依赖与元数据(≈ package.json)
├── Cargo.lock      # 锁定依赖版本(≈ package-lock.json)
└── src/
    └── main.rs     # 入口文件(≈ index.js)

添加依赖

bash 复制代码
# JavaScript
npm install express

# Rust
cargo add axum tokio --features full
部分 含义
cargo add Cargo 的子命令,用于向 Cargo.toml 添加依赖(≈ npm install
axum 第一个要添加的 crate:Web 框架(类似 Express)
tokio 第二个要添加的 crate:异步运行时(类似 Node.js 的事件循环引擎)
--features full 为添加的依赖启用名为 full 的特性

什么是 --features full?

Rust crate 可以通过特性(features)来模块化功能,避免编译不必要的代码:

tokio 的 full 特性:启用所有子模块(rt-multi-thread、macros、net、time、fs 等),方便开发时"一把梭",不用逐个挑选

axum 的 full 特性:启用所有可选功能(如 multipart、ws、macros 等)

Cargo.toml 示例:

toml 复制代码
[package]
name = "my-api"
version = "0.1.0"
edition = "2024"

[dependencies]
axum = "0.8"
tokio = { version = "1.49", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }

二、核心语法对比

1. 变量声明

JavaScript Rust
let / const / var let(可变)/ let mut(可变)
动态类型 静态类型,编译器推断
javascript 复制代码
// JavaScript
const name = "Alice";
let count = 0;
count = 1; // ✅
name = "Bob"; // ❌ TypeError
rust 复制代码
// Rust
let name = "Alice";      // 默认不可变(≈ const)
let mut count = 0;       // 显式声明可变
count = 1;               // ✅
name = "Bob";            // ❌ 编译错误!

关键区别: Rust 中变量默认不可变。要修改必须加 mut,这比 JS 的 const 更严格,能在编译期防止意外修改。

2. 数据类型

JavaScript Rust
number i32, i64, f32, f64(明确位数)
string String(堆分配,可变)/ &str(字符串切片)
boolean bool
null / undefined Option<T>(显式空值处理)
Array Vec<T>(动态数组)
Object struct(结构体)
rust 复制代码
// Rust 基础类型
let age: i32 = 25;           // 32位整数
let price: f64 = 19.99;      // 64位浮点
let is_active: bool = true;
let names: Vec<String> = vec!["Alice".to_string(), "Bob".to_string()];

// 结构体(≈ JS Object)
struct User {
    name: String,
    age: u32,
}

let user = User {
    name: String::from("Alice"),
    age: 30,
};

3. 函数定义

javascript 复制代码
// JavaScript
function add(a, b) {
    return a + b;
}

const greet = (name) => `Hello, ${name}!`;
rust 复制代码
// Rust
fn add(a: i32, b: i32) -> i32 {
    a + b  // 最后一行表达式自动返回(无需 return;无分号)
}

fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

注意: Rust 函数参数和返回值必须标注类型。表达式末尾无分号表示返回值。

4. 控制流

两者非常相似:

javascript 复制代码
// JavaScript
if (score >= 90) {
    console.log("A");
} else if (score >= 80) {
    console.log("B");
} else {
    console.log("C");
}

for (let i = 0; i < 5; i++) {
    console.log(i);
}

const result = age >= 18 ? "adult" : "minor";
rust 复制代码
// Rust
if score >= 90 {
    println!("A");
} else if score >= 80 {
    println!("B");
} else {
    println!("C");
}

for i in 0..5 {
    println!("{}", i);
}

let result = if age >= 18 { "adult" } else { "minor" };

Rust 中 if 是表达式,可以返回值赋值给变量,比 JS 的三元运算符更强大。


三、核心概念:所有权 vs 垃圾回收

这是 Rust 与 JavaScript 最大的区别

JavaScript ------ 垃圾回收(GC)

javascript 复制代码
let a = { name: "Alice" };
let b = a;        // 引用拷贝,两者指向同一对象
b.name = "Bob";   // a.name 也变成 "Bob"
// JS 自动回收不再引用的内存,但时机不确定

Rust ------ 所有权系统

rust 复制代码
let a = String::from("Alice");
let b = a;        // 所有权转移!a 不再有效
// println!("{}", a);  // ❌ 编译错误!a 已被 move
println!("{}", b);    // ✅ "Alice"

三条所有权规则:

  1. 每个值有且只有一个所有者
  2. 当所有者离开作用域,值被自动释放
  3. 所有权可以转移(move)、借用(borrow)或复制(copy)

借用(Borrowing)

如果不想转移所有权,使用引用:

rust 复制代码
let s = String::from("hello");

// 不可变借用(≈ JS 只读引用)
let len = calculate_length(&s);
println!("{} 长度是 {}", s, len);  // ✅ s 仍可用

// 可变借用
let mut s = String::from("hello");
change(&mut s);  // 同时只能有一个可变借用

fn calculate_length(s: &String) -> usize {
    s.len()      // 不获取所有权,只是借用
}

fn change(s: &mut String) {
    s.push_str(", world");
}
方法 来源 说明
String::from("hello") std::string::String &str 字面量创建一个可变的 String
s.len() std::string::String(通过 Deref 调用 str::len 返回字符串字节长度
s.push_str(", world") std::string::String 追加字符串切片,不获取所有权

类比: JS 中你随意传递对象引用;Rust 中编译器会追踪谁拥有数据、谁在借用,确保没有悬垂指针或数据竞争。

四、错误处理

JavaScript ------ try/catch + throw

javascript 复制代码
function divide(a, b) {
    if (b === 0) throw new Error("除零错误");
    return a / b;
}

try {
    const result = divide(10, 0);
} catch (err) {
    console.error(err.message);
}

Rust ------ Result<T, E>Option<T>

Rust 强制你处理错误,没有 try/catch

rust 复制代码
// 可能失败的函数返回 Result
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("除零错误"))
    } else {
        Ok(a / b)
    }
}

// 方式 1:match 显式处理
match divide(10.0, 0.0) {
    Ok(result) => println!("结果: {}", result),
    Err(e) => println!("错误: {}", e),
}

// 方式 2:? 传播错误(≈ await 的自动传播)(最常用)
fn calculate() -> Result<f64, String> {
    let r1 = divide(10.0, 2.0)?;  // 失败时自动返回 Err
    let r2 = divide(r1, 5.0)?;
    Ok(r2)
}

// 方式 3:unwrap(生产环境慎用,会 panic)
let result = divide(10.0, 2.0).unwrap();

match 是 Rust 模式匹配的核心语法,配合 Result<T, E> 实现了编译期强制错误处理。它的本质是:

"告诉我所有可能的情况,否则我不让你编译通过。"

Option<T> ------ 替代 null:

rust 复制代码
fn find_user(id: u32) -> Option<User> {
    if id == 1 {
        Some(User { name: "Alice".to_string(), age: 30 })
    } else {
        None  // 没有 null,只有显式的 None
    }
}

// 安全访问
if let Some(user) = find_user(1) {
    println!("找到: {}", user.name);
}

对比总结: JS 的错误可能在运行时抛出;Rust 的错误是类型系统的一部分,必须在编译期处理,杜绝了未捕获异常。

五、异步编程

JavaScript ------ 事件循环 + Promise

javascript 复制代码
async function fetchData() {
    const response = await fetch("https://api.example.com/data");
    const data = await response.json();
    return data;
}

Rust ------ async/await + 运行时(Tokio)

Rust 没有内置事件循环,需要显式引入运行时(如 Tokio):

rust 复制代码
use tokio;

// #[tokio::main] 宏设置异步运行时(≈ Node.js 启动事件循环)
#[tokio::main]
async fn main() {
    let result = fetch_data().await;
    println!("{:?}", result);
}

async fn fetch_data() -> Result<String, reqwest::Error> {
    let resp = reqwest::get("https://api.example.com/data").await?;
    let body = resp.text().await?;
    Ok(body)
}

宏的作用就是消除样板代码
#tokio::main 是 Rust 的属性宏(Attribute Macro),属于 Rust 宏系统的一部分。它由 tokio crate 提供,作用是自动为你的异步 main 函数设置 Tokio 运行时。

部分 含义
#[] Rust 的属性语法(类似 JavaScript 的装饰器 @decorator
tokio::main 宏的名称,表示这是 tokio 库中名为 main 的宏

宏展开后的实际代码(大致等价):

rust 复制代码
#[tokio::main]
async fn main() {
    println!("Hello");
    let result = some_async_function().await;
}
// -----
fn main() {
    // 创建 Tokio 运行时(多线程版本)
    let rt = tokio::runtime::Runtime::new().unwrap();
    
    // 在运行时中执行 async main 函数
    rt.block_on(async {
        println!("Hello");
        let result = some_async_function().await;
    })
}

关键区别: JS 的 async/await 基于单线程事件循环;Rust 的 async/await 由 Tokio 调度到线程池,实现真正的多核并行,且编译器保证线程安全。

六、实战:REST API 对比

JavaScript (Express)

javascript 复制代码
const express = require('express');
const app = express();
app.use(express.json());

app.get('/users/:id', (req, res) => {
    const user = { id: req.params.id, name: "Alice" };
    res.json(user);
});

app.listen(3000, () => console.log('Server on 3000'));

Rust (Axum + Tokio)

rust 复制代码
use axum::{
    routing::get,
    Router,
    extract::Path,
    Json,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;

#[derive(Serialize)]
struct User {
    id: String,
    name: String,
}

async fn get_user(Path(id): Path<String>) -> Json<User> {
    Json(User {
        id,
        name: "Alice".to_string(),
    })
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/users/:id", get(get_user));

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("Listening on {}", addr);
    
    let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

七、生态与工具链对比

维度 JavaScript Rust
包管理 npm (2M+ 包) Cargo (crates.io, 150K+ 包)
构建工具 Webpack/Vite/Rollup Cargo(内置,无需配置)
代码检查 ESLint + Prettier Clippy(lint)+ rustfmt(格式化)
测试 Jest/Vitest cargo test(内置)
文档 JSDoc cargo doc + /// 注释自动生成
Web 框架 Express/Nest/Fastify Axum/Actix-web/Rocket
ORM Prisma/Drizzle/Sequelize Diesel/SQLx/SeaORM
前端 React/Vue/Angular Leptos/Yew/Dioxus(编译为 WASM)

趋势: 2026 年,Rust 正在大量替代 JavaScript 工具链底层------SWC(转译)、Turbopack(打包)、Biome(代码检查)等,性能提升 10~100 倍。

八、性能与资源对比

指标 JavaScript (Node.js) Rust
CPU 密集型任务 单线程,Worker Threads 辅助 5~10 倍性能提升,真多核并行
内存占用 GC 开销大,不可预测 无 GC,精确内存控制
启动时间 需 Node 运行时 单二进制文件,零依赖
P99 延迟 ~67ms (Bun) / 更高 (Node) ~18ms
内存使用 ~720MB (高负载) ~180MB (高负载)

九、何时选择 Rust?

选择 Rust 选择 JavaScript
高性能后端服务、API 网关 快速原型、MVP 开发
系统工具、CLI、边缘计算 全栈 Web 应用、前端交互
区块链、金融科技、游戏服务器 需要大量 npm 生态集成的项目
WebAssembly 模块 团队以 JS 为主,追求开发速度
内存安全要求极高的场景 实时协作、快速迭代的业务系统

十、学习路径建议(JS → Rust)

复制代码
第 1 周:语法基础
  ├── 变量、类型、函数、控制流
  └── 对比练习:把一段 JS 代码改写成 Rust

第 2 周:所有权与借用
  ├── 理解 move、borrow、lifetime
  └── 练习:实现字符串处理工具

第 3 周:错误处理与集合
  ├── Result、Option、Vec、HashMap
  └── 练习:命令行待办事项应用

第 4 周:异步与 Web
  ├── Tokio、Axum、serde
  └── 练习:用 Rust 重写一个 Express API

第 5~6 周:项目实战
  ├── 选择一个真实项目(如文件处理器、爬虫、微服务)
  └── 部署到服务器(体验单二进制部署的快感)

总结

Rust 不是 JavaScript 的替代品,而是互补工具 。JavaScript 以开发速度和生态广度见长,Rust 以性能、内存安全和可靠性取胜。现代团队越来越多采用混合架构:JS/TS 负责产品层和快速迭代,Rust 负责性能关键的核心引擎,通过 WebAssembly 或 napi-rs 无缝集成。

相关推荐
梦醒沉醉1 小时前
13、Rust程序设计语言——认识Cargo和Crates.io
rust
其美杰布-富贵-李1 小时前
第 6 篇:相机与 OrbitControls
前端·javascript·three.js
步行cgn2 小时前
carList.forEach(System.out::println)
java·开发语言·mybatis
进击的程序猿~2 小时前
Go 内存分配与垃圾回收源码深度学习手册
开发语言·后端·golang
geovindu2 小时前
go:Bit Operation Algorithm
开发语言·后端·算法·golang·位运算法
Hilaku3 小时前
当 AI 一天写完一周的代码,我们还剩什么优势?
前端·javascript·程序员
无责任此方_修行中3 小时前
换个版本号就能升级?可没那简单:pnpm 11 升级踩坑记
javascript·后端·npm
尘世中一位迷途小书童3 小时前
无人机航线里的云台角度怎么调?Cesium 做了个视锥 + 鹰眼联动
前端·javascript·数据可视化
张元清3 小时前
React useMeasure Hook:用 ResizeObserver 测量 DOM 元素 (2026)
javascript·react.js