现在位置: 首页 > Rust 教程 > 正文

Rust 简介

Rust 是一门由 Mozilla Research 主导开发的系统级编程语言,以内存安全零成本抽象无畏并发为核心设计目标。

Rust 诞生于 2006 年,最初是 Mozilla 员工 Graydon Hoare 的个人项目,后于 2009 年由 Mozilla 正式赞助并组建团队开发。

Rust 的设计初衷是解决 C/C++ 在系统编程中长期存在的痛点:内存安全问题(空指针、悬垂指针、缓冲区溢出)和并发编程的复杂性(数据竞争、死锁)。

Rust 的设计哲学是「在编译期消灭 bug」——通过强大的类型系统和所有权机制,让编译器帮你检查出内存错误和数据竞争,而不是等到运行时才发现问题。

Rust 语言吉祥物是一只名叫 Ferris 的螃蟹,亲切可爱,代表 Rust 社区友好、包容的文化。

Rust 的关键设计目标可以归纳为以下几点:

设计目标说明
内存安全(无 GC)通过所有权系统在编译期保证内存安全,无需垃圾回收器
零成本抽象高级语言特性在编译后无运行时开销,与手写底层代码性能一致
无畏并发类型系统和所有权规则在编译期杜绝数据竞争
实用性既能写操作系统内核,也能写 Web 应用,覆盖全栈场景
强类型推断编译器自动推导大部分类型,减少样板代码
丰富的工具链Cargo(包管理+构建)、rustfmt(格式化)、clippy(代码检查)开箱即用

Rust 语言的发展历史

Rust 从个人项目到全球广泛采用的系统语言,走过了近二十年的发展历程。

时间版本/事件说明
2006 年个人项目Graydon Hoare 开始设计 Rust 语言
2009 年Mozilla 赞助Mozilla 正式支持 Rust 开发并组建团队
2010 年首次公开在 Mozilla 峰会上首次对外公开 Rust 编译器(OCaml 编写)
2011 年自举完成Rust 编译器用 Rust 自身重写,摆脱 OCaml 依赖
2015 年 5 月Rust 1.0首个稳定版本发布,标志着语言正式可用于生产环境
2018 年 12 月Rust 2018 Edition首个 Edition 发布,优化语法和模块系统(NLL 借用检查器落地)
2019 年异步生态成形async/await 语法稳定,Tokio 等异步运行时成熟
2021 年Rust 基金会成立AWS、Google、华为、微软、Mozilla 联合成立 Rust 基金会
2021 年 10 月Rust 2021 Edition引入 disjoint capture、IntoIterator for arrays 等改进
2022 年Linux 内核采用Linux 6.1 正式合并 Rust 支持,Rust 成为内核开发第二语言
2024 年 2 月Rust 2024 Edition进一步优化语言一致性和开发者体验

2021 年 Rust 基金会的成立是一个里程碑事件。AWS、Google、华为、微软和 Mozilla 五家科技巨头共同承诺保障 Rust 的长期独立发展,消除了社区对「单一公司主导」的担忧。

Rust 采用六周一个稳定版本的发布节奏,同时通过Edition机制(每 3 年左右发布一次)处理不兼容的语言变更。同一个项目可以混合使用不同 Edition 的 crate,编译器保证向后兼容。


Rust 语言的核心特性

Rust 的特性集合围绕「安全、高效、实用」三个维度展开。以下逐一拆解 Rust 最核心的语言特性。

所有权系统(Ownership)

所有权是 Rust 最独特、最重要的特性。它在编译期管理内存,无需垃圾回收器,也无需手动 malloc/free。

所有权有三大规则:

规则说明含义
每个值有且仅有一个所有者一个值在同一时刻只能被一个变量拥有杜绝 double-free 和悬垂指针
所有者离开作用域,值被释放变量离开其作用域时,Rust 自动调用 drop 释放内存无需手动管理内存
同一时刻:要么一个可变引用,要么多个不可变引用读写互斥,读读共存编译期杜绝数据竞争

代码层面的直观感受:当变量被赋值给另一个变量时,所有权发生转移(move),原变量失效。

实例

fn main() {
    // s1 获得字符串的所有权(数据存储在堆上)
    let s1 = String::from("Hello, RUNOOB!");

    // 所有权从 s1 转移到 s2,此后 s1 不再有效
    let s2 = s1;

    // println!("{}", s1); // 编译错误!s1 已被 moved
    println!("{}", s2);    // 正确:s2 现在是所有者

    // 基本类型(存储在栈上)实现了 Copy trait,不会发生 move
    let x = 42;
    let y = x;  // x 仍然有效,因为 i32 实现了 Copy
    println!("x = {}, y = {}", x, y); // 都能使用

    // 不可变引用:可以同时存在多个
    let s3 = &s2; // 不可变借用
    let s4 = &s2; // 另一个不可变借用,允许
    println!("s3 = {}, s4 = {}", s3, s4);

    // 可变引用:同一时刻只能有一个
    let mut data = String::from("runoob");
    let r = &mut data; // 可变借用
    r.push_str("!");   // 通过可变引用修改原值
    println!("修改后: {}", r);
    // s3 和 s4 的作用域已结束,所以可以创建可变引用
}

所有权系统是 Rust 学习曲线的「陡坡」。初学者常常与编译器「战斗」,但一旦理解,你会发现那些被编译器拦截的错误,在 C/C++ 中可能就是致命的运行时 bug。

模式匹配(Pattern Matching)

Rust 的 match 表达式极其强大,编译器会穷尽性检查所有情况,确保不会遗漏任何分支。

实例

fn main() {
    // match 与数字匹配
    let score = 85;
    match score {
        90..=100 => println!("RUNOOB 评级: A"), // 范围匹配
        60..=89  => println!("RUNOOB 评级: B"),
        0..=59   => println!("RUNOOB 评级: C"),
        _ => println!("无效分数"),                // 通配符,匹配所有剩余情况
    }

    // match 解构元组
    let pair = (3, 7);
    match pair {
        (0, y) => println!("第一个是 0,第二个是 {}", y),
        (x, 0) => println!("第一个是 {},第二个是 0", x),
        (x, y) => println!("两个值: {} 和 {}", x, y),
    }
}

枚举与 Option/Result

Rust 的枚举(enum)可以携带数据,配合 match 使用能实现安全且表达力强大的控制流。

标准库中的 Option<T>Result<T, E> 是两个最核心的枚举类型,分别处理「可能为空」和「可能出错」的场景——Rust 没有 null 和异常。

实例

use std::fs;

fn main() {
    // Option<T>: 值可能存在也可能不存在(替代 null)
    let numbers = vec![10, 20, 30];
    let first = numbers.get(0);   // 返回 Option<&i32>
    let missing = numbers.get(5); // 返回 Option<&i32>

    match first {
        Some(&val) => println!("第一个元素: {}", val),
        None => println!("没有找到"),
    }

    // 更简洁的写法:if let
    if let Some(&val) = missing {
        println!("找到: {}", val);
    } else {
        println!("索引 5 不存在");  // 预期走这个分支
    }

    // Result<T, E>: 操作可能成功也可能失败(替代异常)
    let content = fs::read_to_string("config.txt");
    match content {
        Ok(text) => println!("文件内容: {}", text),
        Err(e) => println!("读取失败: {}", e),
    }
}

Rust 没有 null 值,也没有 try-catch 异常机制。Option 和 Result 是编译期强制处理的——如果你忘记检查,代码无法通过编译。这从根本上消灭了空指针异常和未捕获异常。

零成本抽象

Rust 的高阶特性(迭代器、闭包、泛型)在编译后与手写的底层循环完全等价,不产生额外运行时开销。

这意味着你可以用类似函数式的风格写代码,同时获得和 C 一样的高性能。

实例

fn main() {
    let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

    // 迭代器链式调用 —— 看起来像高级抽象
    // 编译后展开为与手写 for 循环等价的机器码
    let result: Vec<i32> = data
        .iter()            // 获取迭代器
        .filter(|&x| x % 2 == 0) // 筛选偶数
        .map(|&x| x * x)         // 求平方
        .collect();              // 收集为 Vec

    println!("{:?}", result); // [4, 16, 36, 64, 100]
}

无畏并发(Fearless Concurrency)

Rust 的类型系统和所有权规则在编译期杜绝数据竞争。Send 和 Sync trait 自动标记类型是否可以安全地在线程间传递或共享。

如果你试图在多线程中共享非线程安全的数据,编译器会直接报错——而不是等到线上运行时才出现诡异的并发 bug。

实例

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    // Arc(原子引用计数)用于在多线程间共享所有权
    // Mutex 提供互斥访问
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for i in 0..10 {
        // clone Arc,使每个线程持有同一份数据的引用
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            // lock() 获取互斥锁,返回 MutexGuard
            let mut num = counter.lock().unwrap();
            *num += 1; // 通过解引用修改内部值
            println!("线程 {} 累加完成,当前值: {}", i, *num);
        });
        handles.push(handle);
    }

    // 等待所有线程完成
    for handle in handles {
        handle.join().unwrap();
    }

    println!("最终计数: {}", *counter.lock().unwrap());
}

Cargo:一体化的构建工具

Cargo 是 Rust 的官方包管理器和构建系统,集成了依赖管理、编译、测试、文档生成和发布功能。

对比 C/C++ 生态中需要手动配置 CMake/Makefile 和包管理器的分散体验,Cargo 提供了「开箱即用」的一体化体验。

命令功能典型场景
cargo new project创建新项目初始化项目,自动生成 Cargo.toml 和 src/main.rs
cargo build编译项目(debug 模式)开发调试
cargo build --release编译项目(优化模式)生产发布,开启所有编译器优化
cargo run编译并运行快速测试代码
cargo test运行测试单元测试、集成测试、文档测试
cargo check快速检查代码能否编译(不生成二进制)IDE 中的实时语法检查
cargo doc --open生成并打开文档浏览依赖库的 API 文档
cargo clippy代码风格检查(lint)发现不推荐写法和潜在问题
cargo fmt代码格式化统一代码风格
cargo publish发布到 crates.io发布开源库

cargo check 是开发中非常有用的命令。它只检查语法和类型,不生成可执行文件,速度比 cargo build 快很多。大多数 IDE 的 Rust 插件在保存文件时都会自动执行 cargo check。


Rust 语言的应用领域

Rust 凭借内存安全和高性能的优势,正在多个领域迅速扩展。

系统编程与高性能服务端

这是 Rust 的「主场」——适合需要极致性能且对内存安全有高要求的场景。异步运行时 Tokio 和 Web 框架 Actix WebAxum 在高并发场景中表现卓越。

WebAssembly

Rust 是编译到 WebAssembly 的最佳语言之一。它的零成本抽象和无 GC 特性让 Wasm 产物体积小、性能高。

前端框架 YewLeptos 允许用 Rust 编写 SPA 应用。Figma、Cloudflare Workers 等都在生产环境中重度使用 Rust + Wasm。

嵌入式与 IoT

Rust 的 no_std 模式允许在没有操作系统的裸机上运行,适合 MCU、传感器等资源受限设备。

RTIC(实时中断驱动并发框架)和 Embassy(异步嵌入式框架)是嵌入式 Rust 的代表项目。

CLI 工具

Rust 编译为独立二进制文件且性能优异,造就了大量明星 CLI 工具:

工具功能替代对象
ripgrep (rg)极速代码搜索grep
fd快速文件搜索find
bat带语法高亮的文件查看cat
delta语法高亮的 Git diffgit diff
zoxide智能目录跳转cd
dust磁盘空间分析du

区块链与 Web3

Rust 在区块链领域有重要地位。Solana 链的核心用 Rust 编写,SuiAptosPolkadot(Substrate) 等新公链也均以 Rust 为主要开发语言。

数据库与数据基础设施

高性能数据库和数据处理系统是 Rust 的优势领域:TiKV(分布式 KV 存储,TiDB 的存储层)、SurrealDBPolars(DataFrame 库)、Databend(云数据仓库)等均使用 Rust 构建。

操作系统与基础设施

Rust 已进入 Linux 内核(6.1+),Google 在 Android 中使用 Rust 重写蓝牙和 Wi-Fi 栈,微软也在 Windows 内核中使用 Rust 重写部分组件。

业余项目方面,Redox OS 是一个完全用 Rust 从头编写的类 Unix 操作系统。


快速开始

Rust 官方提供了极其方便的安装工具 rustup,一条命令搞定编译器、包管理器和文档。

安装 Rust

# Unix/macOS
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 验证安装
$ rustc --version
rustc 1.85.0 (4d91de4e4 2025-02-17)

$ cargo --version
cargo 1.85.0

第一个 Rust 程序

使用 Cargo 创建项目是最推荐的方式,它会自动生成标准的项目结构。

# 创建新项目
$ cargo new hello_runoob
     Created binary (application) `hello_runoob` package

# 项目结构
hello_runoob/
├── Cargo.toml     # 项目元数据和依赖配置
└── src/
    └── main.rs    # 程序入口文件

Cargo.toml 是项目的配置文件,类似于 Node.js 的 package.json:

实例

[package]
name = "hello_runoob"           # 包名(必填,使用下划线分隔)
version = "0.1.0"               # 版本号(必填,遵循语义化版本)
edition = "2024"                # Rust Edition(必填,影响语言语法规则)

[dependencies]
# 在这里声明项目依赖,例如:
# serde = { version = "1.0", features = ["derive"] }

默认生成的 main.rs 包含一个最简单的 Hello World 程序:

实例

// 文件路径:src/main.rs
// Rust 程序入口:每个可执行程序必须有 main 函数

fn main() {
    // println! 是一个宏(注意后面的 ! 符号)
    // 宏在编译时展开为更复杂的代码
    println!("Hello, RUNOOB!");
    println!("欢迎来到 Rust 语言的世界");
}
# 编译并运行
$ cargo run
   Compiling hello_runoob v0.1.0
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
     Running `target/debug/hello_runoob`
Hello, RUNOOB!
欢迎来到 Rust 语言的世界

基础语法概览

Rust 的语法借鉴了函数式语言和系统语言的优点。以下快速浏览最常用的语法元素,每个示例都是可直接运行的完整程序。

变量与可变性

Rust 中的变量默认不可变(immutable)。这迫使开发者明确标注哪些数据会变化,让代码意图更清晰,也让编译器更容易优化。

实例

fn main() {
    // 不可变变量(默认):声明后不能重新赋值
    let name = "RUNOOB";
    // name = "other"; // 编译错误!不可变变量不能重新赋值
    println!("名称: {}", name);

    // 可变变量:使用 mut 关键字显式声明
    let mut score = 0;
    println!("初始分数: {}", score);
    score = 100;       // 可以修改
    println!("更新后分数: {}", score);

    // 变量遮蔽(Shadowing):用 let 重新声明同名变量
    let x = 5;
    let x = x + 1;      // 新变量 x 遮蔽旧 x,值为 6
    let x = x * 2;      // 再次遮蔽,值为 12
    println!("x 的最终值: {}", x);

    // 常量:编译时确定,必须标注类型,命名习惯用全大写
    const MAX_POINTS: u32 = 100_000;
    println!("最大点数: {}", MAX_POINTS);
}

变量遮蔽(Shadowing)和 mut 不是一回事。遮蔽是用 let 重新声明一个同名新变量,可以改变类型;而 mut 只是让同一个变量允许修改值,类型不变。

基本数据类型

Rust 是静态类型语言,编译器在编译时必须知道所有变量的类型。

类别类型示例说明
有符号整数i8, i16, i32, i64, i128, isizelet x: i32 = -42;默认整数类型为 i32
无符号整数u8, u16, u32, u64, u128, usizelet y: u64 = 100;usize 用于索引和长度
浮点数f32, f64let pi: f64 = 3.14;默认浮点类型为 f64
布尔值boollet ok: bool = true;true 或 false
字符charlet c = 'R';4 字节,表示 Unicode 标量值
元组(T1, T2, ...)let t: (i32, f64) = (10, 3.14);固定长度,类型可不同
数组[T; N]let a: [i32; 3] = [1, 2, 3];固定长度,类型相同,分配在栈上
切片&[T]let s: &[i32] = &a[..];对数组或 Vec 的引用视图
动态数组Vec<T>let v: Vec<i32> = vec![1,2,3];可变长度,分配在堆上
字符串String / &strlet s = String::from("hi");String 可修改,&str 是引用

控制流程

Rust 的控制流程包括 ifloopwhileformatch。其中 if 和 loop 可以作为表达式返回值。

实例

fn main() {
    let score = 75;

    // if 是表达式,可以返回值(各分支必须返回相同类型)
    let grade = if score >= 90 {
        "A"
    } else if score >= 60 {
        "B"
    } else {
        "C"
    };
    println!("RUNOOB 评级: {}", grade);

    // loop 无限循环(用 break 退出,可带返回值)
    let mut count = 0;
    let result = loop {
        count += 1;
        if count == 5 {
            break count * 2; // break 后跟值,作为 loop 的返回值
        }
    };
    println!("loop 返回值: {}", result);

    // while 条件循环
    let mut n = 3;
    while n > 0 {
        println!("倒计时: {}", n);
        n -= 1;
    }

    // for in 遍历(最常用)
    let items = ["apple", "banana", "cherry"];
    for (i, item) in items.iter().enumerate() {
        println!("第 {} 个: {}", i + 1, item);
    }

    // for in 范围遍历
    for num in 1..=3 { // 1..=3 表示 1, 2, 3(包含两端)
        println!("数字: {}", num);
    }
}

函数

Rust 函数使用 fn 关键字声明,参数和返回值必须显式标注类型。最后一个表达式的值即为返回值(无需 return 关键字)。

实例

// 普通函数:接收两个 i32,返回它们的和
fn add(a: i32, b: i32) -> i32 {
    a + b  // 最后一个表达式自动作为返回值(注意末尾没有分号)
}

// 多返回值:使用元组返回多个值
fn divide(a: f64, b: f64) -> (f64, String) {
    if b == 0.0 {
        return (0.0, String::from("错误:除数不能为零"));
    }
    (a / b, String::from("OK")) // 返回元组
}

// 泛型函数:使用 trait bound 限定类型参数的行为
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    // 调用普通函数
    let sum = add(10, 20);
    println!("10 + 20 = {}", sum);

    // 调用多返回值函数
    let (result, msg) = divide(10.0, 2.0);
    println!("10 / 2 = {} ({})", result, msg);

    // 除零情况
    let (result, msg) = divide(10.0, 0.0);
    println!("10 / 0 = {} ({})", result, msg);

    // 调用泛型函数
    let nums = vec![3, 7, 2, 9, 5];
    println!("RUNOOB 最大数: {}", largest(&nums));
}

结构体与 impl 块

Rust 使用 struct 定义数据结构,用 impl 块为结构体实现方法。这与 Go 的方法定义方式类似,但语言组织上更紧凑。

实例

// 定义结构体(类似 C 的 struct,但功能更强)
struct User {
    name: String,         // String 类型,拥有数据的所有权
    email: String,
    active: bool,
    login_count: u64,     // u64 无符号 64 位整数
}

// impl 块为 User 实现方法
impl User {
    // 关联函数(类似静态方法):创建新实例
    fn new(name: String, email: String) -> User {
        User {
            name,
            email,
            active: true,     // 默认值
            login_count: 0,   // 默认值
        }
    }

    // 方法:&self 表示对实例的不可变引用
    fn summary(&self) -> String {
        format!("{} ({}) - 登录次数: {}", self.name, self.email, self.login_count)
    }

    // 方法:&mut self 表示对实例的可变引用
    fn login(&mut self) {
        self.login_count += 1;
    }
}

fn main() {
    // 使用关联函数创建实例
    let mut user = User::new(
        String::from("runoob"),
        String::from("runoob@example.com"),
    );

    println!("{}", user.summary());

    // 模拟登录
    user.login();
    user.login();
    println!("登录 {} 次", user.login_count);
}

常用示例

以下通过两个接近真实场景的完整示例,展示 Rust 在实际开发中的典型用法。

构建 HTTP API 服务

使用 Axum 框架(基于 Tokio 异步运行时)构建一个带 JSON 响应的 HTTP API。

首先在 Cargo.toml 中添加依赖:

# Cargo.toml
[dependencies]
axum = "0.8"              # Web 框架
tokio = { version = "1", features = ["full"] }  # 异步运行时
serde = { version = "1", features = ["derive"] } # 序列化
serde_json = "1"           # JSON 支持

实例

// 文件路径:src/main.rs
use axum::{
    extract::Query,
    response::Json,
    routing::get,
    Router,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

// 请求参数结构体(反序列化)
#[derive(Deserialize)]
struct HelloParams {
    name: Option<String>, // Option 表示可选参数
}

// 响应结构体(序列化)
#[derive(Serialize)]
struct ApiResponse {
    code: u16,
    message: String,
    data: Option<HashMap<String, String>>, // 可选的数据字段
}

// 处理 GET /hello 请求
// Query(HelloParams) 自动从 URL 查询参数中提取 name
async fn hello_handler(Query(params): Query<HelloParams>) -> Json<ApiResponse> {
    let name = params.name.unwrap_or_else(|| String::from("RUNOOB"));

    let mut data = HashMap::new();
    data.insert("name".to_string(), name.clone());

    Json(ApiResponse {
        code: 200,
        message: format!("你好,{}!欢迎访问 Rust HTTP 服务", name),
        data: Some(data),
    })
}

// #[tokio::main] 宏将 async fn main 转换为 Tokio 运行时
#[tokio::main]
async fn main() {
    // 构建路由
    let app = Router::new()
        .route("/hello", get(hello_handler));

    // 绑定地址并启动服务
    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080")
        .await
        .unwrap();

    println!("RUNOOB 服务器启动,监听地址: http://localhost:8080");
    println!("访问示例: http://localhost:8080/hello?name=Rust开发者");

    axum::serve(listener, app).await.unwrap();
}
$ cargo run
RUNOOB 服务器启动,监听地址: http://localhost:8080
访问示例: http://localhost:8080/hello?name=Rust开发者

# 使用 curl 测试
$ curl http://localhost:8080/hello?name=Rust开发者
{"code":200,"message":"你好,Rust开发者!欢迎访问 Rust HTTP 服务","data":{"name":"Rust开发者"}}

$ curl http://localhost:8080/hello
{"code":200,"message":"你好,RUNOOB!欢迎访问 Rust HTTP 服务","data":{"name":"RUNOOB"}}

Axum 是 Rust 生态中最流行的 Web 框架之一,由 Tokio 团队维护。它充分利用 Rust 的类型系统,将路由参数提取、状态共享等操作在编译期进行检查,大幅减少了运行时错误。

文件批量处理工具

这是一个接近真实场景的 CLI 工具:递归遍历目录,统计所有文件的行数和大小,按扩展名分类汇总。

在 Cargo.toml 中添加依赖:

# Cargo.toml
[dependencies]
walkdir = "2"  # 递归遍历目录

实例

// 文件路径:src/main.rs
use std::collections::HashMap;
use std::fs;
use walkdir::WalkDir; // 递归遍历目录的第三方库

fn main() {
    let path = "."; // 从当前目录开始扫描

    // 统计信息:按文件扩展名分组
    // HashMap<扩展名, (文件数量, 总行数, 总大小字节)>
    let mut stats: HashMap<String, (usize, usize, u64)> = HashMap::new();

    // WalkDir 递归遍历目录,自动跳过隐藏文件和 .git 目录
    for entry in WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok()) // 过滤掉无法访问的条目
        .filter(|e| e.file_type().is_file()) // 只处理文件
    {
        // 获取文件扩展名
        let ext = entry
            .path()
            .extension()
            .and_then(|e| e.to_str())
            .unwrap_or("(无扩展名)")
            .to_lowercase();

        // 读取文件内容(仅文本文件,二进制文件可能乱码但不影响统计)
        if let Ok(content) = fs::read_to_string(entry.path()) {
            let line_count = content.lines().count();
            let size = entry.metadata().map(|m| m.len()).unwrap_or(0);

            let (count, lines, total_size) = stats.entry(ext).or_insert((0, 0, 0));
            *count += 1;
            *lines += line_count;
            *total_size += size;
        }
    }

    // 按文件数量降序排列并输出
    let mut sorted: Vec<_> = stats.into_iter().collect();
    sorted.sort_by(|a, b| b.1 .0.cmp(&a.1 .0));

    println!("=== RUNOOB 目录文件统计 ===");
    println!("{:<15} {:>8} {:>10} {:>12}", "扩展名", "文件数", "总行数", "总大小(bytes)");
    println!("{}", "-".repeat(50));
    for (ext, (count, lines, size)) in &sorted {
        println!("{:<15} {:>8} {:>10} {:>12}", ext, count, lines, size);
    }
}
$ cargo run --release
=== RUNOOB 目录文件统计 ===
扩展名            文件数       总行数   总大小(bytes)
--------------------------------------------------
rs                  12        856        28432
toml                 3         45         1534
md                   2        120         3412
html                 1        510        14256
(无扩展名)            1          8          210

常用工具与命令速查

除了 Cargo 自带的功能外,Rust 生态提供了一系列提升开发效率的工具:

命令功能说明
rustup update更新 Rust 工具链升级 rustc、cargo 等到最新稳定版
rustup component add rustfmt安装代码格式化工具保存时自动格式化代码
rustup component add clippy安装代码检查工具发现不推荐的写法和潜在 bug
rustup doc打开本地文档离线查看标准库和 The Book
cargo add <crate>添加依赖自动编辑 Cargo.toml 并获取最新版本
cargo update更新依赖版本根据 Cargo.toml 的版本约束更新 Cargo.lock
cargo tree查看依赖树可视化项目的完整依赖关系
cargo bench运行基准测试测量代码性能
cargo install <tool>安装 Rust 编写的 CLI 工具如 cargo install ripgrep

注意事项与常见问题

以下是 Rust 初学者最容易面对的挑战和最佳实践建议。

与编译器「战斗」是正常的

Rust 的学习曲线是公认的前陡后缓。所有权和借用检查是初学时最大的障碍。

当你写的代码被编译器反复拒绝时,不要灰心——这恰恰说明编译器在帮你拦截潜在的运行时 bug。随着经验积累,你会逐渐习惯用 Rust 的方式思考,编译器将成为你最可靠的伙伴而非敌人。

String 和 &str 的区别

这是 Rust 初学者最容易混淆的概念。String 是拥有所有权的堆分配字符串(可修改),&str 是对字符串数据的不可变引用(借用的视图),通常是 &String 自动解引用而来。

实例

fn main() {
    // String: 拥有所有权,可修改,在堆上
    let mut s = String::from("Hello");

    // &str: 引用,不可修改,通常作为函数参数
    let slice: &str = "RUNOOB"; // 字符串字面量本身就是 &str

    s.push_str(", world!"); // String 可以修改
    println!("{}", s);
    println!("{}", slice);

    // 函数参数选用 &str(而非 String)更灵活
    greet("RUNOOB");          // 可以直接传 &str
    greet(&s);                // 也可以通过 &String 自动转为 &str
}

fn greet(name: &str) {
    println!("你好,{}!", name);
}

错误处理:panic! vs Result

Rust 区分不可恢复的错误(使用 panic!,程序终止)和可恢复的错误(使用 Result,调用方决定如何处理)。

在库代码中,应返回 Result 而非 panic!,让调用方有选择权。在应用程序中,可根据具体情况决定。

unsafe:慎用但必要

Rust 提供了 unsafe 关键字,允许进行裸指针操作、调用外部函数(FFI)等底层操作。

unsafe 并非「禁用安全检查」,而是让开发者承担编译器无法自动保证的那部分安全责任。大多数应用程序代码无需使用 unsafe——如果你发现自己在频繁使用它,可能是设计上需要调整。

编译时间较长

Rust 以编译时间长而闻名,尤其是大型项目。这是因为编译器在编译期做了大量检查(所有权分析、生命周期验证、trait 解析等)和优化。

缓解措施:使用 cargo check 代替 cargo build 进行日常开发;将大型 crate 拆分为子 crate 以利用增量编译;使用 sccache 缓存编译结果;升级硬件(尤其是 SSD 和更多 CPU 核心)。

学习路径建议

对于 Rust 初学者,推荐以下学习顺序:

  • 通读 The Rust Programming Language(官方书,免费),重点理解所有权章节
  • 在 Rust Playground(play.rust-lang.org)上边读边写,即时获得编译器反馈
  • 通过 Rustlings 做小型练习,巩固语法
  • 阅读 Rust by Example,通过实例学习
  • 尝试用 Rust 重写一个自己熟悉的小项目,在实践中理解 Rust 的设计权衡

总结

Rust 以内存安全(无 GC)零成本抽象无畏并发三大核心优势,在系统编程、高性能服务、WebAssembly 和区块链等领域占据了独特的生态位。

虽然学习曲线较陡,但编译器严格的检查在长期项目中转化为极高的代码质量和可维护性——这也是为什么越来越多的公司和开源项目选择 Rust。

Rust 的优势和适用场景总结如下:

维度Rust 的表现
学习曲线较陡(所有权系统需要理解和适应),但概念稳定,学会后受益终身
编译速度较慢(大量编译期检查),但有 cargo check 和增量编译缓解
运行时性能与 C/C++ 同级,远超带 GC 的语言
内存安全编译期保证,无需 GC,无空指针、悬垂指针、缓冲区溢出
并发安全编译期杜绝数据竞争,无惧并发
生态成熟度快速增长,在系统编程、WebAssembly、区块链领域已非常成熟
最适合系统编程、高性能后端、WebAssembly、嵌入式、区块链、CLI 工具、数据库
不太适合快速原型开发(编译时间)、GUI 桌面应用、游戏开发(与成熟引擎相比)