Rust

内存安全的系统编程 - 零成本抽象,编译时保证,无垃圾回收

TL;DR

一句话:Rust 给你 C++ 的性能,同时在编译时保证内存安全。

核心优势

  • 没有垃圾回收,没有内存泄漏
  • 没有空指针,没有数据竞争
  • 零成本抽象——只为使用的功能付出代价
  • 编译器错误信息友好清晰

Philosophy

Rust 的核心原则是无畏并发

  • 所有权 - 每个值只有一个所有者。所有者离开作用域,值被释放。
  • 借用 - 引用允许临时访问而不获取所有权
  • 生命周期 - 编译器追踪引用的有效期
  • 没有 null - 用 Option<T> 代替。强制你处理”无值”情况。

编译器很严格但很有帮助。如果能编译通过,(大部分情况下)就能正确运行。

Quick Start

安装

# macOS/Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

验证(稳定版:1.85.0,2024 版)

rustc --version  # rustc 1.85.0
cargo --version

第一个程序

cargo new hello
cd hello
cargo run

这会创建并运行:

// src/main.rs
fn main() {
    println!("Hello, world!");
}

Language Essentials

变量与类型

// 默认不可变
let name = "Alice";    // &str(字符串切片)
let age = 25;          // i32
let height = 1.75;     // f64
let active = true;     // bool

// 可变
let mut count = 0;
count += 1;

// 显式类型
let items: Vec<i32> = vec![1, 2, 3];
let data: HashMap<String, i32> = HashMap::new();

控制流

// if-else(它是表达式!)
let status = if age >= 18 { "成年人" } else { "未成年" };

// match(穷尽的模式匹配)
match age {
    0..=12 => println!("儿童"),
    13..=19 => println!("青少年"),
    _ => println!("成年人"),
}

// for 循环
for item in items.iter() {
    println!("{}", item);
}

for i in 0..5 {  // 0, 1, 2, 3, 4
    println!("{}", i);
}

函数

fn greet(name: &str) -> String {
    format!("Hello, {}!", name)  // 没有分号 = 返回值
}

// 带错误处理
fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err("除数不能为零".to_string())
    } else {
        Ok(a / b)
    }
}

错误处理

// Result<T, E> 处理可恢复错误
let result = divide(10, 2);
match result {
    Ok(value) => println!("结果: {}", value),
    Err(e) => println!("错误: {}", e),
}

// ? 运算符传播错误
fn read_file() -> Result<String, io::Error> {
    let content = fs::read_to_string("file.txt")?;
    Ok(content)
}

// Option<T> 处理可选值
let first = items.first();  // Option<&i32>
if let Some(value) = first {
    println!("第一个: {}", value);
}

所有权与借用

// 所有权转移
let s1 = String::from("hello");
let s2 = s1;  // s1 移动到 s2
// println!("{}", s1);  // 错误:s1 不再有效

// 通过引用借用
let s1 = String::from("hello");
let len = calculate_length(&s1);  // 借用 s1
println!("{} 长度是 {}", s1, len);  // s1 仍然有效

fn calculate_length(s: &String) -> usize {
    s.len()
}

Gotchas

所有权转移

let s1 = String::from("hello");
let s2 = s1;
// println!("{}", s1);  // 错误!s1 已移动

// 解决:克隆或使用引用
let s2 = s1.clone();  // 或
let s2 = &s1;

可变引用是独占的

let mut s = String::from("hello");
let r1 = &mut s;
// let r2 = &mut s;  // 错误:不能有两个可变引用
r1.push_str(" world");

String vs &str

let owned: String = String::from("owned");  // 堆上,可变
let slice: &str = "borrowed";               // 栈/静态,不可变

迭代器是惰性的

// 这什么也不做
items.iter().map(|x| x * 2);

// 需要收集或消费
let doubled: Vec<_> = items.iter().map(|x| x * 2).collect();

When to Choose

适合

  • 系统编程(操作系统、驱动、嵌入式)
  • WebAssembly
  • CLI 工具(ripgrep、fd、bat)
  • 性能关键的服务

不适合

  • 快速原型(用 Python、Go)
  • 简单 Web 应用(学习曲线太陡)
  • 刚接触系统编程的团队

对比

方面RustC++Go
内存安全不安全GC
速度最快最快
学习困难更困难简单
编译

Next Steps

Ecosystem

包管理器(Cargo)

cargo new project     # 创建项目
cargo build           # 编译
cargo run             # 构建并运行
cargo test            # 运行测试
cargo add serde       # 添加依赖

主流 Crate

  • Web:Actix-web、Axum、Rocket
  • 异步:Tokio、async-std
  • 序列化:serde
  • CLI:clap、structopt