TL;DR
Rust 在编译时保证内存安全,提供 C/C++ 级别的性能——无垃圾回收器、无空指针异常、无数据竞争。掌握所有权(每个值只有一个所有者)、借用(&T 只读借用,&mut T 可变借用)和生命周期(引用的有效时长)。用 Result<T,E> 处理可恢复错误,Option<T> 处理可空值。异步 Web 服务使用基于 Tokio 运行时的 Axum 或 Actix-web。Cargo 是一流的包管理器:cargo new、cargo build、cargo test、cargo doc。
核心要点
- Rust 通过所有权系统在编译时消除内存安全问题,无需垃圾回收器,性能与 C/C++ 相当。
- 每个值有且只有一个所有者;借用规则:同一时刻要么有多个 &T,要么只有一个 &mut T,两者不能共存。
- 用 Result<T,E> 处理可恢复错误,用 Option<T> 处理可空值;? 运算符自动传播错误,避免 unwrap() panic。
- trait 是 Rust 的多态机制;泛型约束(T: Trait)实现零成本静态分发,dyn Trait 支持运行时动态分发。
- Tokio 是最主流的异步运行时;Axum 和 Actix-web 是构建高性能 Web API 的首选框架。
- Cargo 是一流的包管理器和构建工具:cargo new、cargo build、cargo test、cargo clippy、cargo doc 覆盖完整开发工作流。
1. 什么是 Rust?为什么开发者喜爱它?
Rust 是 Mozilla 在 2010 年开始开发、2015 年正式发布的系统编程语言。它的目标是提供 C/C++ 级别的性能,同时通过独特的所有权系统在编译时保证内存安全,无需垃圾回收器。Rust 连续九年(2016-2024)在 Stack Overflow 开发者调查中荣登"最受喜爱语言"榜首,已被 Linux 内核、Windows、Android、Firefox、Cloudflare、AWS、Meta、Google 等大型项目和公司采用。
Rust 解决的核心问题:C/C++ 中约 70% 的安全漏洞来自内存安全问题(悬空指针、缓冲区溢出、数据竞争)。Rust 通过编译时检查完全消除这类问题,而不是依赖运行时的垃圾回收或手动内存管理。
// Your first Rust program
fn main() {
// Variables are immutable by default
let name = "Rust";
let mut counter = 0; // mut makes it mutable
println!("Hello from {}!", name);
// Loops, conditions, and expressions
for i in 1..=5 {
counter += i;
}
println!("Sum 1..5 = {}", counter); // 15
// Everything is an expression
let result = if counter > 10 {
"greater than 10"
} else {
"10 or less"
};
println!("Result: {}", result);
}Rust vs C++ vs Go — Language Comparison
=========================================
Feature Rust C++ Go
------- ---- --- --
Memory Safety Compile-time Manual GC (runtime)
Garbage Collector No No Yes
Null Safety Option<T> Raw pointers nil (runtime)
Concurrency Safety Compile-time Manual Goroutines+GC
Performance C-equivalent C-equivalent ~20-50% slower
Compile Speed Slower Medium Very fast
Runtime Zero-size Zero-size Small runtime
Error Handling Result<T,E> Exceptions/codes errors (multiple return)
Generics Yes (monomorphic) Yes (templates) Yes (1.18+)
Async/Await Yes (Tokio) Yes (C++20) Goroutines
Package Manager Cargo vcpkg/Conan/... Go modules
Learning Curve Steep Very steep Gentle
Best For Systems, WebAsm Systems, games Cloud, CLIs, APIs
Choose Rust when:
- Memory safety is critical (security software, OS kernels)
- Zero-cost abstractions with no GC pauses (game engines, real-time)
- WebAssembly targets
- Embedded systems or resource-constrained environments
Choose C++ when:
- Existing large C++ codebase
- Graphics/game engines (Unreal, etc.)
- Maximum ecosystem maturity
Choose Go when:
- Cloud-native microservices and CLIs
- Team prefers simplicity over raw performance
- Fast build times are a priority2. 安装 Rust 与创建第一个项目
Rust 通过 rustup 工具链管理器安装,rustup 可以管理多个 Rust 版本(stable、beta、nightly)和交叉编译目标。安装后,rustc 编译器、Cargo 包管理器和标准库文档全部就绪。
安装与工具链管理
# Install rustup (macOS/Linux)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Windows: download rustup-init.exe from https://rustup.rs
# Verify installation
rustc --version # rustc 1.76.0 (07dca489a 2024-02-04)
cargo --version # cargo 1.76.0
# Update Rust
rustup update
# Install nightly for experimental features
rustup install nightly
rustup default nightly
# Add a compilation target (cross-compile)
rustup target add wasm32-unknown-unknown # WebAssembly
rustup target add x86_64-pc-windows-gnu # Windows from Linux
# Install useful tools
cargo install cargo-watch # auto-rebuild on change
cargo install cargo-expand # expand macros
cargo install cargo-audit # security vulnerability audit
cargo install cargo-flamegraph # profiling
rustup component add clippy # linter
rustup component add rustfmt # formatterCargo:Rust 的包管理器与构建系统
# Create a new binary project
cargo new my-project
cd my-project
# Create a new library project
cargo new my-lib --lib
# Project structure
my-project/
Cargo.toml # project manifest (like package.json)
Cargo.lock # exact dependency versions (commit to VCS for bins)
src/
main.rs # entry point for binary
tests/ # integration tests
benches/ # benchmarks
examples/ # example programs
# Build commands
cargo build # debug build (fast compile, slow runtime)
cargo build --release # optimized build (slow compile, fast runtime)
cargo run # build + run
cargo run --release # optimized run
cargo test # run all tests
cargo test my_function # run tests matching a name
cargo doc --open # build and open documentation
cargo clippy # lint with Clippy
cargo fmt # format code
cargo check # fast type-check without producing binary
cargo clean # remove build artifacts
# Cargo.toml example
[package]
name = "my-project"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"
reqwest = { version = "0.11", features = ["json"] }
[dev-dependencies]
mockall = "0.11"
[profile.release]
opt-level = 3
lto = true # link-time optimization3. 所有权、借用与生命周期
所有权系统是 Rust 最独特的特性,也是理解 Rust 的关键。它使 Rust 不需要垃圾回收器就能保证内存安全,也是初学者遇到的主要挑战——借用检查器(borrow checker)会在编译时拒绝不安全的代码。
所有权规则与移动语义
fn main() {
// Rule 1: each value has one owner
let s1 = String::from("hello"); // s1 owns the String
// Rule 2: when owner goes out of scope, value is dropped
{
let s2 = String::from("world");
println!("{}", s2); // s2 valid here
} // s2 dropped here, memory freed automatically
// Move semantics: ownership transfer
let s3 = s1; // s1 is MOVED to s3
// println!("{}", s1); // ERROR: s1 is no longer valid!
println!("{}", s3); // OK
// Clone: explicit deep copy
let s4 = s3.clone();
println!("{} and {}", s3, s4); // both valid
// Copy types (stack-allocated primitives): implicit copy
let x = 5;
let y = x; // x is COPIED, not moved
println!("{} and {}", x, y); // both valid
// Types that implement Copy: i32, f64, bool, char, tuples of Copy types
}
// Ownership and functions
fn takes_ownership(s: String) { // s moves into this function
println!("{}", s);
} // s is dropped here
fn gives_ownership() -> String { // returns ownership to caller
String::from("mine")
}借用与引用
fn main() {
let s = String::from("hello");
// Immutable borrow: &T
let len = calculate_length(&s); // pass reference, not ownership
println!("Length of s is: {}", len); // s still valid here
// Mutable borrow: &mut T
let mut s2 = String::from("hello");
change(&mut s2);
println!("{}", s2); // "hello, world"
// Borrow rules demonstration
let mut s3 = String::from("test");
let r1 = &s3; // immutable borrow 1 - OK
let r2 = &s3; // immutable borrow 2 - OK (multiple &T allowed)
println!("{} {}", r1, r2); // last use of r1, r2
// r1 and r2 are no longer used after this point (NLL: Non-Lexical Lifetimes)
let r3 = &mut s3; // mutable borrow - OK (r1, r2 no longer active)
r3.push_str(" done");
// println!("{} {}", r1, r3); // ERROR: can't mix &T and &mut T
}
fn calculate_length(s: &String) -> usize {
s.len() // can read, cannot modify
}
fn change(s: &mut String) {
s.push_str(", world"); // can modify through &mut
}生命周期标注
// Lifetime annotation: explicit when compiler can't infer
// 'a is a lifetime parameter ('a is conventional, any letter works)
// Without annotation this would fail — compiler doesn't know
// if the return value lives as long as x or y
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
// 'a means: return value lives at least as long as the
// shorter of x and y's lifetimes
}
// Struct with a reference field — requires lifetime annotation
struct Excerpt<'a> {
text: &'a str, // text must live as long as this struct
}
impl<'a> Excerpt<'a> {
fn announce(&self, msg: &str) -> &str {
println!("Attention: {}", msg);
self.text // lifetime elision: return has same lifetime as &self
}
}
fn main() {
let s1 = String::from("long string");
let result;
{
let s2 = String::from("xyz");
result = longest(s1.as_str(), s2.as_str());
println!("Longest: {}", result); // OK: result used within s2's scope
}
// println!("{}", result); // ERROR: s2 dropped, result might dangle
}4. 结构体、枚举与模式匹配
Rust 的枚举(enum)比大多数语言的枚举更强大——每个变体可以携带不同类型和数量的数据,本质上是代数数据类型(ADT)。结合穷举的模式匹配,可以写出类型安全且表达力极强的代码。
结构体与方法
// Named struct
#[derive(Debug, Clone)] // auto-implement Debug and Clone traits
struct User {
username: String,
email: String,
active: bool,
login_count: u32,
}
// Tuple struct (named tuple)
struct Point(f64, f64, f64);
struct Color(u8, u8, u8);
// Unit struct (no fields, useful for traits)
struct AlwaysEqual;
// Implementing methods
impl User {
// Associated function (no self): constructor
fn new(username: String, email: String) -> Self {
User {
username,
email,
active: true,
login_count: 0,
}
}
// Method with immutable borrow
fn is_active(&self) -> bool {
self.active
}
// Method with mutable borrow
fn deactivate(&mut self) {
self.active = false;
}
// Method that consumes self
fn into_email(self) -> String {
self.email
}
}
fn main() {
let mut user = User::new(
String::from("alice"),
String::from("alice@example.com"),
);
println!("{:?}", user); // Debug output
user.deactivate();
// Struct update syntax
let user2 = User {
email: String::from("bob@example.com"),
..user // remaining fields from user (moved)
};
}枚举与模式匹配
// Enum with data in variants (ADT)
#[derive(Debug)]
enum Shape {
Circle(f64), // radius
Rectangle(f64, f64), // width, height
Triangle { base: f64, height: f64 }, // named fields
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
}
}
}
// Message enum — common Rust pattern
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
// Pattern matching — must be exhaustive
fn process_message(msg: Message) {
match msg {
Message::Quit => println!("Quitting"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(text) => println!("Write: {}", text),
Message::ChangeColor(r, g, b) => println!("Color: rgb({},{},{})", r, g, b),
}
}
// if let — match a single pattern
fn check_coin(coin: Option<u32>) {
if let Some(value) = coin {
println!("Got {} cents", value);
} else {
println!("No coin");
}
}
// while let — loop until pattern fails
fn drain_stack(stack: &mut Vec<i32>) {
while let Some(top) = stack.pop() {
println!("{}", top);
}
}5. 错误处理:Result 与 Option
Rust 没有异常机制。错误处理通过两种类型完成:Option<T> 处理值可能不存在的情况,Result<T, E> 处理操作可能失败的情况。这种显式的错误处理迫使开发者在编写代码时就考虑错误路径,是 Rust 程序可靠性高的重要原因。
use std::fs;
use std::num::ParseIntError;
// Option<T> — value might be absent
fn find_first_even(numbers: &[i32]) -> Option<i32> {
numbers.iter().find(|&&n| n % 2 == 0).copied()
}
// Result<T, E> — operation might fail
fn parse_number(s: &str) -> Result<i32, ParseIntError> {
s.trim().parse::<i32>()
}
// ? operator — early return on error
fn read_and_parse(path: &str) -> Result<i32, Box<dyn std::error::Error>> {
let content = fs::read_to_string(path)?; // returns Err if file missing
let number = content.trim().parse::<i32>()?; // returns Err if not a number
Ok(number * 2)
}
// Handling Results with combinators
fn demonstrate_combinators() {
// map: transform Ok value
let doubled = parse_number("21").map(|n| n * 2);
println!("{:?}", doubled); // Ok(42)
// and_then: chain fallible operations
let result = parse_number("10")
.and_then(|n| if n > 0 { Ok(n) } else { Err("0".parse::<i32>().unwrap_err()) });
// unwrap_or: provide default value
let val = parse_number("abc").unwrap_or(0);
println!("{}", val); // 0
// unwrap_or_else: compute default lazily
let val2 = parse_number("abc").unwrap_or_else(|e| {
eprintln!("Parse error: {}", e);
-1
});
// Option combinators
let name: Option<&str> = Some(" alice ");
let trimmed = name.map(|s| s.trim());
let upper = name.map(|s| s.trim()).map(|s| s.to_uppercase());
// ok_or: convert Option to Result
let result: Result<&str, &str> = name.ok_or("no name provided");
}自定义错误类型与 anyhow
use std::fmt;
// Custom error type — best practice for library code
#[derive(Debug)]
enum AppError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
Custom(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::Io(e) => write!(f, "IO error: {}", e),
AppError::Parse(e) => write!(f, "Parse error: {}", e),
AppError::Custom(msg) => write!(f, "Error: {}", msg),
}
}
}
// impl From<> enables automatic ? conversion
impl From<std::io::Error> for AppError {
fn from(e: std::io::Error) -> Self { AppError::Io(e) }
}
impl From<std::num::ParseIntError> for AppError {
fn from(e: std::num::ParseIntError) -> Self { AppError::Parse(e) }
}
fn process() -> Result<(), AppError> {
let content = std::fs::read_to_string("data.txt")?; // auto-converts io::Error
let num: i32 = content.trim().parse()?; // auto-converts ParseIntError
if num < 0 {
return Err(AppError::Custom("number must be positive".into()));
}
Ok(())
}
// --- OR use anyhow for application code ---
// Cargo.toml: anyhow = "1"
use anyhow::{Context, Result, bail, anyhow};
fn process_with_anyhow(path: &str) -> Result<i32> {
let content = std::fs::read_to_string(path)
.with_context(|| format!("Failed to read file: {}", path))?;
let num: i32 = content.trim().parse()
.context("File does not contain a valid integer")?;
if num < 0 {
bail!("number {} must be non-negative", num);
}
Ok(num)
}6. Trait 与泛型
Trait 定义共享行为,类似其他语言的接口,但支持默认实现和运算符重载。泛型(Generics)加上 trait 约束(bounds)实现零成本抽象:编译器为每个具体类型生成专用代码(单态化),运行时无任何额外开销。
// Define a trait
trait Summary {
fn summarize_author(&self) -> String; // required method
// Default implementation
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
#[derive(Debug)]
struct Article { title: String, author: String, content: String }
impl Summary for Article {
fn summarize_author(&self) -> String { self.author.clone() }
// Override default implementation
fn summarize(&self) -> String {
format!("{}, by {} — {}", self.title, self.author, &self.content[..50])
}
}
// Generics with trait bounds — static dispatch (zero cost)
fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
// Multiple bounds with + syntax
fn notify_debug<T: Summary + std::fmt::Debug>(item: &T) {
println!("{:?}", item);
println!("{}", item.summarize());
}
// Where clause for complex bounds
fn complex_function<T, U>(t: &T, u: &U) -> String
where
T: std::fmt::Display + Clone,
U: Summary + std::fmt::Debug,
{
format!("{} {}", t, u.summarize())
}
// impl Trait syntax (sugar for generics in simple cases)
fn notify_impl(item: &impl Summary) {
println!("{}", item.summarize());
}
// Return impl Trait — return type is opaque
fn make_summarizable() -> impl Summary {
Article {
title: String::from("Test"),
author: String::from("Alice"),
content: String::from("Content here"),
}
}Trait Objects — 动态分发
// dyn Trait — runtime polymorphism via vtable (like virtual in C++)
struct Tweet { username: String, content: String }
impl Summary for Tweet {
fn summarize_author(&self) -> String { format!("@{}", self.username) }
}
// Vec of different types sharing a trait
fn print_all(items: &[Box<dyn Summary>]) {
for item in items {
println!("{}", item.summarize());
}
}
fn main() {
let article = Box::new(Article {
title: "Rust Rules".into(),
author: "Alice".into(),
content: "Lorem ipsum dolor sit amet consectetur adipiscing elit...".into(),
});
let tweet = Box::new(Tweet {
username: "bob".into(),
content: "Rust is amazing!".into(),
});
let items: Vec<Box<dyn Summary>> = vec![article, tweet];
print_all(&items);
}
// Important standard library traits to implement:
// Clone — explicit deep copy: #[derive(Clone)]
// Copy — implicit bitwise copy (stack types): #[derive(Copy, Clone)]
// Debug — {:?} formatting: #[derive(Debug)]
// Display — {} formatting: impl fmt::Display
// Iterator — .iter(), for loops: impl Iterator
// From/Into — type conversions: impl From<T>
// PartialEq — == operator: #[derive(PartialEq)]
// Hash — use as HashMap key: #[derive(Hash, Eq, PartialEq)]7. 闭包与迭代器
Rust 的迭代器是惰性的(lazy):调用 .map()、.filter() 等适配器不会立即执行,只有在终结方法(.collect()、.sum()、.for_each())被调用时才会执行。这种设计使得链式操作和底层循环一样高效——编译器能够完全优化掉迭代器抽象。
// Closures — anonymous functions that capture their environment
fn main() {
// Basic closure syntax
let add = |x, y| x + y; // type inferred
let square = |x: i32| -> i32 { x * x };
println!("{}", add(3, 4)); // 7
// Capture by reference (Fn)
let threshold = 10;
let is_big = |n: &i32| n > &threshold; // captures threshold by ref
// Capture by value (move)
let text = String::from("hello");
let contains_hello = move |s: &str| s.contains(&text); // moves text
// text is moved into closure, cannot use it here
// Three closure traits:
// Fn — borrows immutably, can be called multiple times
// FnMut — borrows mutably, can be called multiple times
// FnOnce — consumes captured values, called only once
let mut count = 0;
let mut increment = || { count += 1; count }; // FnMut
println!("{}", increment()); // 1
println!("{}", increment()); // 2
}// Iterator methods — zero-cost abstractions
fn iterator_examples() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// map: transform each element
let doubled: Vec<i32> = numbers.iter().map(|&n| n * 2).collect();
// filter: keep elements matching predicate
let evens: Vec<&i32> = numbers.iter().filter(|&&n| n % 2 == 0).collect();
// filter_map: filter + map in one step
let parsed: Vec<i32> = vec!["1", "two", "3", "four"]
.iter()
.filter_map(|s| s.parse().ok())
.collect();
// fold/reduce: accumulate a value
let sum = numbers.iter().fold(0, |acc, &n| acc + n); // 55
let sum2: i32 = numbers.iter().sum(); // shorthand for sum
// chain: concatenate iterators
let a = vec![1, 2, 3];
let b = vec![4, 5, 6];
let combined: Vec<i32> = a.iter().chain(b.iter()).copied().collect();
// zip: pair two iterators
let pairs: Vec<(i32, i32)> = a.iter().zip(b.iter()).map(|(&x, &y)| (x, y)).collect();
// enumerate: add index
for (i, val) in numbers.iter().enumerate() {
println!("Index {}: {}", i, val);
}
// flat_map: map then flatten
let words = vec!["hello world", "foo bar"];
let all_words: Vec<&str> = words.iter().flat_map(|s| s.split_whitespace()).collect();
// take/skip: limit or offset
let first_three: Vec<i32> = numbers.iter().take(3).copied().collect();
let skip_first: Vec<i32> = numbers.iter().skip(7).copied().collect();
// any/all: boolean queries
let has_even = numbers.iter().any(|&n| n % 2 == 0); // true
let all_positive = numbers.iter().all(|&n| n > 0); // true
// max, min, count
let max = numbers.iter().max(); // Some(10)
let count = numbers.iter().count(); // 10
}8. 异步 Rust:Tokio 运行时与 async/await
Rust 的 async/await 是语言级别的语法糖,编译器将 async 函数转换为状态机(实现 Future trait)。与 JavaScript 的 async/await 不同,Rust 的 Future 是惰性的——创建一个 Future 不会立即执行任何操作,必须由运行时(如 Tokio)轮询才会执行。Tokio 是最主流的异步运行时,提供多线程工作窃取调度器。
# Cargo.toml
[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"use tokio::time::{sleep, Duration};
use tokio::sync::{mpsc, Mutex};
use std::sync::Arc;
// #[tokio::main] initializes the Tokio runtime
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Basic async/await
let result = fetch_data("https://api.example.com/data").await?;
println!("{:?}", result);
// Concurrent tasks with tokio::spawn
let handle1 = tokio::spawn(async {
sleep(Duration::from_millis(100)).await;
println!("Task 1 done");
42
});
let handle2 = tokio::spawn(async {
sleep(Duration::from_millis(50)).await;
println!("Task 2 done");
24
});
let (r1, r2) = tokio::join!(handle1, handle2); // await both
println!("Results: {} {}", r1?, r2?);
// tokio::select! — race multiple futures
tokio::select! {
_ = sleep(Duration::from_millis(1000)) => println!("Timed out"),
result = fetch_data("https://api.example.com") => {
println!("Got result: {:?}", result);
}
}
// Shared state with Arc<Mutex<T>>
let counter = Arc::new(Mutex::new(0u32));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
handles.push(tokio::spawn(async move {
let mut lock = counter.lock().await;
*lock += 1;
}));
}
for h in handles { h.await?; }
println!("Final count: {}", counter.lock().await);
// Message passing with mpsc channels
let (tx, mut rx) = mpsc::channel::<String>(32);
tokio::spawn(async move {
tx.send("hello".to_string()).await.unwrap();
tx.send("world".to_string()).await.unwrap();
});
while let Some(msg) = rx.recv().await {
println!("Received: {}", msg);
}
Ok(())
}
async fn fetch_data(url: &str) -> anyhow::Result<serde_json::Value> {
let response = reqwest::get(url).await?;
let json = response.json::<serde_json::Value>().await?;
Ok(json)
}9. Rust 常用数据结构:Vec、HashMap 与 BTreeMap
Rust 标准库提供了一套经过精心设计的数据结构。Vec<T> 是最常用的动态数组;HashMap<K, V> 提供 O(1) 平均查找,使用 SipHash 哈希算法(防哈希洪水攻击);BTreeMap<K, V> 保持键的排序顺序,查找时间复杂度 O(log n),适合需要有序遍历的场景。
use std::collections::{HashMap, BTreeMap, HashSet, BTreeSet, VecDeque, BinaryHeap};
// Vec<T> — dynamic array (most common collection)
fn vec_examples() {
let mut v: Vec<i32> = Vec::new();
let v2 = vec![1, 2, 3, 4, 5]; // macro shorthand
v.push(1); // O(1) amortized
v.pop(); // O(1)
v.insert(0, 99); // O(n)
v.remove(0); // O(n)
v.retain(|&x| x > 0); // keep elements matching predicate
v.sort(); // in-place sort O(n log n)
v.sort_by(|a, b| b.cmp(a)); // reverse sort
v.dedup(); // remove consecutive duplicates (sort first)
// Slices — view into a Vec
let slice: &[i32] = &v2[1..3]; // [2, 3]
let first = v2.first(); // Option<&i32>
let last = v2.last(); // Option<&i32>
let len = v2.len();
let is_empty = v2.is_empty();
// Pre-allocate capacity for performance
let mut v3: Vec<i32> = Vec::with_capacity(1000);
for i in 0..1000 { v3.push(i); } // no reallocation
}// HashMap<K, V> — O(1) average lookup
fn hashmap_examples() {
let mut scores: HashMap<String, i32> = HashMap::new();
// Insert
scores.insert("Alice".to_string(), 100);
scores.insert("Bob".to_string(), 85);
// entry API — insert if not present
scores.entry("Alice".to_string()).or_insert(0);
scores.entry("Carol".to_string()).or_insert(0); // inserts 0
// entry with modification
let count = scores.entry("Alice".to_string()).or_insert(0);
*count += 10; // now 110
// Lookup
let alice = scores.get("Alice"); // Option<&i32>
let bob = scores["Bob"]; // panics if missing!
let dave = scores.get("Dave").copied().unwrap_or(0);
// Iterate
for (name, score) in &scores {
println!("{}: {}", name, score);
}
// Collect from iterator
let map: HashMap<&str, i32> = vec![("a", 1), ("b", 2)]
.into_iter()
.collect();
// Remove
scores.remove("Bob");
let contains = scores.contains_key("Alice"); // true
}
// BTreeMap<K, V> — sorted, O(log n) lookup
fn btreemap_examples() {
let mut map: BTreeMap<&str, i32> = BTreeMap::new();
map.insert("zebra", 1);
map.insert("apple", 2);
map.insert("mango", 3);
// Iterates in sorted key order: apple, mango, zebra
for (k, v) in &map { println!("{}: {}", k, v); }
// Range queries — unique to BTreeMap
use std::ops::Bound::Included;
for (k, v) in map.range("a"..="m") {
println!("{}: {}", k, v); // apple, mango
}
}// Other important collections
fn other_collections() {
// HashSet<T> — unordered unique values, O(1) lookup
let mut set: HashSet<i32> = HashSet::new();
set.insert(1); set.insert(2); set.insert(1); // dedup: {1, 2}
let contains = set.contains(&1); // true
let union: HashSet<_> = set.union(&HashSet::from([2, 3])).collect();
let intersection: HashSet<_> = set.intersection(&HashSet::from([1, 3])).collect();
// BTreeSet<T> — sorted unique values
let bset: BTreeSet<i32> = vec![3, 1, 4, 1, 5].into_iter().collect();
// Iterates in sorted order: 1, 3, 4, 5
// VecDeque<T> — double-ended queue, O(1) push/pop on both ends
let mut deque: VecDeque<i32> = VecDeque::new();
deque.push_back(1);
deque.push_front(0);
let front = deque.pop_front(); // Some(0)
let back = deque.pop_back(); // Some(1)
// BinaryHeap<T> — max-heap, O(log n) insert/pop
let mut heap: BinaryHeap<i32> = BinaryHeap::new();
heap.push(3); heap.push(1); heap.push(4); heap.push(2);
let max = heap.pop(); // Some(4)
}10. Rust Web 开发:Axum 与 Actix-web
Rust 的 Web 框架已经成熟。Axum(由 Tokio 团队维护)和 Actix-web 是目前最主流的两个选择。两者在 TechEmpower 框架基准测试中都位居前列,远超大多数其他语言的框架。
Axum:基于 Tower 生态的现代 Web 框架
# Cargo.toml
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.5", features = ["cors", "trace", "compression-gzip"] }
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio-rustls", "uuid"] }
uuid = { version = "1", features = ["v4", "serde"] }use axum::{
extract::{Path, Query, State, Json},
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post, put, delete},
Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
#[derive(Debug, Clone, Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Debug, Clone, Deserialize)]
struct PaginationQuery {
page: Option<u32>,
per_page: Option<u32>,
}
// Shared application state
type AppState = Arc<RwLock<Vec<User>>>;
#[tokio::main]
async fn main() {
let state: AppState = Arc::new(RwLock::new(vec![
User { id: 1, name: "Alice".into(), email: "alice@example.com".into() },
]));
let app = Router::new()
.route("/users", get(list_users).post(create_user))
.route("/users/:id", get(get_user).put(update_user).delete(delete_user))
.route("/health", get(|| async { "OK" }))
.with_state(state)
.layer(
tower_http::cors::CorsLayer::permissive()
);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
println!("Listening on http://0.0.0.0:3000");
axum::serve(listener, app).await.unwrap();
}
// GET /users?page=1&per_page=10
async fn list_users(
State(state): State<AppState>,
Query(params): Query<PaginationQuery>,
) -> Json<Vec<User>> {
let users = state.read().await;
let page = params.page.unwrap_or(1) as usize;
let per_page = params.per_page.unwrap_or(10) as usize;
let start = (page - 1) * per_page;
let paginated: Vec<User> = users.iter().skip(start).take(per_page).cloned().collect();
Json(paginated)
}
// GET /users/:id
async fn get_user(
State(state): State<AppState>,
Path(id): Path<u32>,
) -> Result<Json<User>, StatusCode> {
let users = state.read().await;
users.iter()
.find(|u| u.id == id)
.cloned()
.map(Json)
.ok_or(StatusCode::NOT_FOUND)
}
// POST /users
async fn create_user(
State(state): State<AppState>,
Json(payload): Json<CreateUser>,
) -> (StatusCode, Json<User>) {
let mut users = state.write().await;
let id = users.len() as u32 + 1;
let user = User { id, name: payload.name, email: payload.email };
users.push(user.clone());
(StatusCode::CREATED, Json(user))
}
// PUT /users/:id
async fn update_user(
State(state): State<AppState>,
Path(id): Path<u32>,
Json(payload): Json<CreateUser>,
) -> Result<Json<User>, StatusCode> {
let mut users = state.write().await;
if let Some(user) = users.iter_mut().find(|u| u.id == id) {
user.name = payload.name;
user.email = payload.email;
Ok(Json(user.clone()))
} else {
Err(StatusCode::NOT_FOUND)
}
}
// DELETE /users/:id
async fn delete_user(
State(state): State<AppState>,
Path(id): Path<u32>,
) -> StatusCode {
let mut users = state.write().await;
let len_before = users.len();
users.retain(|u| u.id != id);
if users.len() < len_before { StatusCode::NO_CONTENT } else { StatusCode::NOT_FOUND }
}Actix-web:高性能 Actor 模型 Web 框架
# Cargo.toml
[dependencies]
actix-web = "4"
actix-cors = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"use actix_web::{
web, App, HttpServer, HttpResponse, Responder,
middleware::Logger,
};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
struct Product {
id: u32,
name: String,
price: f64,
}
#[derive(Deserialize)]
struct CreateProduct {
name: String,
price: f64,
}
async fn get_products() -> impl Responder {
let products = vec![
Product { id: 1, name: "Widget".into(), price: 9.99 },
];
HttpResponse::Ok().json(products)
}
async fn create_product(body: web::Json<CreateProduct>) -> impl Responder {
let product = Product {
id: 1,
name: body.name.clone(),
price: body.price,
};
HttpResponse::Created().json(product)
}
async fn get_product_by_id(path: web::Path<u32>) -> impl Responder {
let id = path.into_inner();
HttpResponse::Ok().json(Product {
id,
name: format!("Product {}", id),
price: 19.99,
})
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.wrap(Logger::default())
.wrap(actix_cors::Cors::permissive())
.service(
web::scope("/api")
.route("/products", web::get().to(get_products))
.route("/products", web::post().to(create_product))
.route("/products/{id}", web::get().to(get_product_by_id))
)
})
.bind("0.0.0.0:8080")?
.run()
.await
}11. Rust 测试:单元测试、集成测试与基准测试
Rust 内置了测试框架,无需额外依赖。单元测试写在与被测代码同一文件的 #[cfg(test)] 模块中,可以访问私有函数。集成测试放在 tests/ 目录,只能访问公共 API。cargo test 命令自动发现和运行所有测试。
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 { Err("Division by zero".to_string()) }
else { Ok(a / b) }
}
// Unit tests — same file, #[cfg(test)] module
#[cfg(test)]
mod tests {
use super::*; // import everything from parent module
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
assert_eq!(add(-1, 1), 0);
assert_ne!(add(1, 1), 3);
}
#[test]
fn test_divide_success() {
let result = divide(10.0, 2.0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 5.0);
}
#[test]
fn test_divide_by_zero() {
let result = divide(10.0, 0.0);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Division by zero");
}
#[test]
#[should_panic(expected = "index out of bounds")]
fn test_panic() {
let v = vec![1, 2, 3];
let _ = v[99]; // intentional panic
}
#[test]
#[ignore] // run with: cargo test -- --ignored
fn expensive_test() {
// slow test skipped by default
}
}
// Integration test: tests/integration_test.rs
// (separate file, can only use public API)
// use my_crate::{add, divide};
// #[test]
// fn test_public_api() {
// assert_eq!(add(10, 20), 30);
// }
// Run specific tests
// cargo test — run all tests
// cargo test test_add — run tests matching "test_add"
// cargo test -- --nocapture — show println! output
// cargo test -- --test-threads=1 — run single-threaded常见问题解答
Rust 的所有权系统是什么?
Rust 的所有权系统是其内存安全的核心机制,无需垃圾回收器。三条规则:1) 每个值有且只有一个所有者;2) 当所有者离开作用域,值被自动释放(drop);3) 值在任意时刻只能有一个可变引用或多个不可变引用,但不能同时存在两者。这些规则在编译时强制执行,消除了悬空指针、重复释放和数据竞争等内存安全问题。
借用和引用有什么区别?
引用(&T)是借用的语法形式,借用是操作的语义描述。不可变借用 &T 允许读取但不允许修改,可以同时存在多个。可变借用 &mut T 允许读写,但在同一作用域内同一数据只能有一个可变借用存在,且不能与不可变借用共存。借用检查器(borrow checker)在编译时验证所有引用在其被使用期间都是有效的,防止悬空引用。
什么是生命周期,什么时候需要标注生命周期?
生命周期描述引用保持有效的作用域范围,防止悬空引用。Rust 编译器通过生命周期省略规则(lifetime elision rules)在大多数情况下自动推断生命周期。需要显式标注的场景:函数返回引用时(返回值的生命周期必须与某个参数生命周期关联)、结构体包含引用字段时、在 impl 块中实现含引用的方法时。语法:fn longest<'a>(x: &'a str, y: &'a str) -> &'a str,表示返回值的生命周期不长于 x 和 y 中较短的那个。
Result 和 Option 有什么区别?
Option<T> 表示一个值可能存在(Some(T))也可能不存在(None),用于可空值场景,例如哈希表查找或可选配置。Result<T, E> 表示操作可能成功(Ok(T))或失败(Err(E)),用于错误处理,例如文件 I/O、网络请求、解析操作。? 运算符可在返回 Result 或 Option 的函数中自动传播错误或 None:遇到 Err 或 None 时立即返回,否则解包值继续执行。生产代码中应优先使用 ? 运算符而非 unwrap(),避免 panic。
trait 和接口有什么区别?
Rust 的 trait 类似于其他语言的接口,但更强大。区别:1) Rust trait 可以有默认方法实现;2) 可以为外部类型实现自己的 trait(孤儿规则:trait 或类型至少有一个在当前 crate 中定义);3) trait object(dyn Trait)支持运行时多态,泛型约束(T: Trait)支持零成本的静态多态;4) trait 可以作为函数参数(impl Trait 语法)或返回类型。标准库中重要的 trait 包括 Clone、Copy、Debug、Display、Iterator、From/Into 和 Serialize/Deserialize(serde)。
Tokio 是什么?如何在 Rust 中编写异步代码?
Tokio 是 Rust 最主流的异步运行时,提供异步 I/O、任务调度、定时器和网络原语。Rust 的 async/await 语法生成状态机(Future),Tokio 运行时负责轮询和执行这些 Future。关键概念:async fn 返回 impl Future;.await 暂停当前任务直到 Future 完成;tokio::spawn 创建并发任务(类似 goroutine);tokio::select! 同时等待多个 Future,取先完成的。使用 #[tokio::main] 宏初始化运行时并运行 async main 函数。
Rust 和 C++ 相比有什么优势?
Rust 相对 C++ 的主要优势:1) 内存安全:编译器通过所有权和借用检查消除悬空指针、缓冲区溢出、数据竞争,而 C++ 依赖程序员手动管理;2) 并发安全:Send 和 Sync trait 使数据竞争在编译时不可能发生;3) 更现代的工具链:Cargo 提供统一的包管理、构建系统和测试框架;4) 更友好的错误提示:Rust 编译器的错误信息明确指出问题所在;5) 无未定义行为(在 safe Rust 中)。性能方面两者相当,底层都可以做零成本抽象,但 C++ 的生态系统和历史代码库更成熟。
如何在 Rust 中构建 Web API?
主流选择是 Axum 或 Actix-web。Axum(tokio-rs/axum)基于 Tower 中间件生态和 Tokio,使用 Extractor 模式解析请求(Path、Query、Json),路由通过 Router::new().route() 定义,适合熟悉 Tower 生态的团队。Actix-web 是高性能 actor 模型框架,性能基准测试中常名列前茅,API 风格更接近传统 Web 框架。两者都使用 serde 进行 JSON 序列化,sqlx 或 SeaORM 连接数据库,tower-http 处理 CORS/日志/压缩中间件。
Rust 核心概念速查表
Rust Core Concepts — Quick Reference
=====================================
Concept Syntax / Notes
------- --------------
Immutable variable let x = 5;
Mutable variable let mut x = 5;
Type annotation let x: i32 = 5;
Ownership transfer let y = x; // x is moved
Immutable borrow let r = &x;
Mutable borrow let r = &mut x; (only one at a time)
Dereference *r
String literal &str (reference, stack)
Owned string String (heap-allocated)
Option Some(val) | None
Result Ok(val) | Err(e)
Error propagation ? (replaces match Err(e) => return Err(e))
Struct struct Name { field: Type }
Tuple struct struct Color(u8, u8, u8);
Enum variant enum Msg { Quit, Move { x: i32 }, Write(String) }
Pattern match match val { Variant(x) => expr, _ => default }
Closure |args| body or |args| -> Type { body }
Generic function fn foo<T: Trait>(x: T) -> T
Lifetime annotation fn foo<'a>(x: &'a str) -> &'a str
Trait definition trait Name { fn method(&self) -> Type; }
Trait implementation impl TraitName for TypeName { ... }
Trait object Box<dyn Trait> / &dyn Trait
Async function async fn foo() -> T { ... }
Await let val = future.await;
Spawn task tokio::spawn(async { ... })
Arc (shared ownership) Arc::new(data) + Arc::clone(&arc)
Mutex (thread safety) Mutex::new(data) + data.lock().await
Vec Vec::new() / vec![1,2,3]
HashMap HashMap::new() + map.entry(k).or_insert(v)
Iterator chain iter.filter(|x| ...).map(|x| ...).collect()
Derive macros #[derive(Debug, Clone, Serialize, Deserialize)]
Test #[test] fn test_name() { assert_eq!(...); }
Conditional compile #[cfg(test)] mod tests { ... }