首页 / 速查手册 / 在线

Rust速查手册

Rust所有权/借用/trait/错误处理速查

速查手册 · 1 次 · 2026-10-04 · 分享 · 全屏

🦀 cargo 工程管理

新建项目

--bin 可执行程序,--lib 库;cargo 自动生成 git 与目录骨架。

cargo new myapp --bin      # 二进制程序 src/main.rs
cargo new mylib --lib      # 库     src/lib.rs
cargo init                 # 在当前目录初始化
构建与运行

开发构建带调试信息,--release 启用优化。

cargo run
cargo run --release
cargo build
cargo build --release
cargo run -- --flag        # -- 之后是传给程序的参数
./target/debug/myapp       # 直接运行产物
添加依赖

cargo add 自动选版本并写入 Cargo.toml。

cargo add serde
cargo add serde --features derive
cargo add tokio --features full
cargo add anyhow@1
cargo remove serde
cargo tree                 # 查看依赖树
检查 / 测试 / 文档

check 只做类型检查不生成代码,反馈最快。

cargo check
cargo test
cargo test it_works        # 按名过滤
cargo doc --open           # 生成并打开文档
cargo bench
格式化与静态检查

rustfmt 统一风格,clippy 给出惯用法建议。

rustup component add rustfmt clippy
cargo fmt
cargo fmt --check
cargo clippy
cargo clippy --fix --allow-dirty
Cargo.toml 常用片段

[dependencies] 写三方库,[dev-dependencies] 仅测试/示例可用。

[package]
name = "myapp"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
anyhow = "1"

[profile.release]
opt-level = 3

🔒 变量与所有权

let / mut / shadow

变量默认不可变,需要修改必须声明 mut;同名遮蔽(shadow)可换类型。

let x = 5;
// x = 6;            // 编译错误:不可变
let mut y = 1;
y += 1;

let spaces = "   ";          // &str
let spaces = spaces.len();   // shadow:变成 usize
const MAX_POINTS: u32 = 100_000;
标量类型

有符号 i8~i64/isize、无符号 u8~u64/usize、浮点 f32/f64。

let i: i32 = -42;
let u: u64 = 100;
let f: f64 = 3.14;
let b: bool = true;
let c: char = '中';          // char 是 Unicode 标量值,4 字节

let dec = 1_000;
let hex = 0xff;
let bin = 0b1010;
let byte: u8 = b'A';

let n = "123".parse::<i32>().unwrap();
所有权 move 与 clone

堆数据赋值即转移所有权,原变量失效;需要深拷贝用 clone()。

let s1 = String::from("hello");
let s2 = s1;              // 所有权移动到 s2
// println!("{}", s1);   // 编译错误:s1 已被移动

let s3 = s2.clone();      // 深拷贝,s2 仍可用
println!("{} {}", s2, s3);

// 函数传参同样会转移所有权
takes_ownership(s3);
// s3 此后不可用
Copy 栈类型与函数返回

固定大小的栈类型(整数、bool、char、元组等)实现 Copy,赋值自动复制。

let x = 5;
let y = x;          // i32 实现 Copy,x 仍可用
println!("{} {}", x, y);

let t1 = (1, 2.0);  // 元素都 Copy,元组也 Copy
let t2 = t1;

fn calc(s: String) -> String {   // 进函数失去所有权,返回重新移交
    s
}

🔗 借用与生命周期

共享借用 & 与可变借用 &mut

同一时刻:多个不可变引用,或唯一一个可变引用,二者互斥。

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

fn push_world(s: &mut String) {
    s.push_str(" world");
}

let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;            // 任意多个不可变借用 OK
println!("{} {}", r1, r2);
let r3 = &mut s;        // r1/r2 此后不再使用才能借用
r3.push('!');
生命周期标注

生命周期不改变存活时间,只是告诉编译器引用之间的关系。

// 返回的引用与 x、y 中较短者存活一致
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

// 结构体持有引用必须标注生命周期
struct Excerpt<'a> {
    part: &'a str,
}
let e = Excerpt { part: "hello world" };
切片 slice

字符串切片类型是 &str;数组/ Vec 也可切片。切片越界会 panic。

let s = String::from("hello world");
let hello: &str = &s[0..5];
let world = &s[6..];
let all = &s[..];

let arr = [10, 20, 30, 40];
let sl: &[i32] = &arr[1..3];    // [20, 30]

fn first_word(s: &str) -> &str {  // 参数用 &str 最通用
    s.split_whitespace().next().unwrap_or("")
}

🏷 struct / enum / trait

结构体与 impl 方法

new 只是约定函数;&self 读、&mut self 改、self 获取所有权。

struct User {
    name: String,
    age: u32,
    active: bool,
}

impl User {
    fn new(name: &str) -> Self {
        Self { name: name.to_string(), age: 0, active: true }
    }
    fn greet(&self) -> String {
        format!("hi, {}", self.name)
    }
    fn birthday(&mut self) {
        self.age += 1;
    }
}

let mut u = User::new("Tom");
元组结构体与元组解构

适合命名字段无意义的轻量包装;单元结构体无字段。

struct Color(u8, u8, u8);
struct Meter(f64);

let red = Color(255, 0, 0);
let Color(r, g, b) = red;
println!("{} {} {}", r, g, b);

fn area(Meter(w): Meter) -> f64 { w * w } // 解构作参数
枚举与 match

每个变体可携带不同类型数据;match 必须穷尽所有情况。

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

let ip = IpAddr::V4(127, 0, 0, 1);

match ip {
    IpAddr::V4(a, b, ..) => println!("IPv4 {}.{}.x.x", a, b),
    IpAddr::V6(s) => println!("IPv6 {}", s),
}

match n {
    1 => println!("一"),
    2 | 3 | 5 => println!("质数"),
    6..=10 => println!("六到十"),
    _ => println!("其他"),
}
Option 与 if let

Rust 没有 null,可能缺失一律用 Option<T>。

fn find_id(name: &str) -> Option<u32> {
    if name == "Tom" { Some(1) } else { None }
}

match find_id("Amy") {
    Some(id) => println!("id = {}", id),
    None => println!("未找到"),
}

// 只关心一种情况时用 if let
if let Some(id) = find_id("Tom") {
    println!("{}", id);
}

let len = find_id("x").unwrap_or(0);
trait 定义与实现

类似接口;可带默认实现,可作为参数/返回类型。

trait Greet {
    fn greet(&self) -> String;
    fn hello(&self) -> String {       // 默认实现
        format!("(默认) {}", self.greet())
    }
}

impl Greet for User {
    fn greet(&self) -> String {
        format!("我是 {}", self.name)
    }
}

// trait 作参数:impl Trait 语法糖
fn print_greet(g: &impl Greet) {
    println!("{}", g.greet());
}

❗ 错误处理 Result / ?

Result 与模式匹配

可恢复错误用 Result<T, E>,没有异常机制。

use std::fs::File;

let f = match File::open("a.txt") {
    Ok(file) => file,
    Err(e) => panic!("打开失败: {e}"),
};

let n: Result<i32, _> = "42".parse::<i32>();
match n {
    Ok(v) => println!("{v}"),
    Err(e) => println!("解析失败: {e}"),
}
? 错误传播运算符

成功取值继续,失败立即 return Err;自动做可转换的错误类型提升。

use std::fs::File;
use std::io::{self, Read};

fn read_username() -> io::Result<String> {
    let mut f = File::open("user.txt")?;   // 失败直接返回 Err
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s.trim().to_string())
}

fn main() -> io::Result<()> {
    let name = read_username()?;
    println!("{name}");
    Ok(())
}
unwrap / expect

原型或确定不会错时使用;生产代码中 expect 要写明原因。

let v = "42".parse::<i32>().unwrap();
let v = "42".parse::<i32>().expect("配置必须是整数");

let home = std::env::var("HOME").unwrap_or_default();
let port: u16 = std::env::var("PORT")
    .ok()
    .and_then(|s| s.parse().ok())
    .unwrap_or(8080);
anyhow 简化应用层错误

应用程序可用 anyhow::Error 统一各种错误;库建议定义自己的错误枚举。

// Cargo.toml: anyhow = "1", thiserror = "1"
use anyhow::{Context, Result};

fn run() -> Result<()> {
    let cfg = std::fs::read_to_string("config.toml")
        .context("读取配置文件失败")?;
    println!("{cfg}");
    Ok(())
}

fn main() -> Result<()> {
    run()
}

📚 集合类型

Vec 动态数组

vec! 宏快速创建;get 返回 Option,不会因越界 panic。

let mut v: Vec<i32> = vec![1, 2, 3];
v.push(4);
v.pop();
v.insert(0, 9);
v.remove(1);
v.extend([5, 6]);

println!("{}", v[0]);
if let Some(x) = v.get(100) {
    println!("{x}");
}
let arr: Vec<i32> = (1..=5).collect();
HashMap

entry API 是“存在则改、不存在则插”的惯用法。

use std::collections::HashMap;

let mut scores = HashMap::new();
scores.insert("Blue", 10);
scores.insert("Red", 50);

if let Some(v) = scores.get("Blue") {
    println!("{v}");
}

// 统计词频
let text = "a b a c b a";
let mut count = HashMap::new();
for w in text.split_whitespace() {
    *count.entry(w).or_insert(0) += 1;
}
scores.remove("Red");
String 与 &str

String 可增长有所有权,&str 是字符串切片(借用)。

let mut s = String::from("foo");
s.push_str("bar");
s.push('!');

let a = String::from("Hello, ");
let b = String::from("Rust");
let c = a + &b;             // a 被移动,&b 自动解引用为 &str
let c = format!("{}-{}", a, b);

let lit: &str = "静态字符串";  // 编译期内嵌,生命周期 'static
let owned = lit.to_string();

🔁 迭代器 Iterator

map / filter / collect

迭代器是惰性的,collect 等消费方法才真正执行。

let v = vec![1, 2, 3, 4];

let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
let evens: Vec<&i32> = v.iter().filter(|x| *x % 2 == 0).collect();
let strings: Vec<String> = v.iter().map(|x| x.to_string()).collect();

// 原地修改用 iter_mut
let mut v = v;
v.iter_mut().for_each(|x| *x += 10);
sum / fold / scan

fold 是通用聚合;sum 需标注结果类型。

let sum: i32 = v.iter().sum();
let product: i32 = v.iter().product();

let s = v.iter().fold(0, |acc, x| acc + x);
let max = v.iter().fold(i32::MIN, |a, x| a.max(*x));

// 求累计和:1, 3, 6, 10
let acc: Vec<i32> = v.iter()
    .scan(0, |state, x| { *state += x; Some(*state) })
    .collect();
enumerate / zip / chain

带索引遍历、双迭代器对齐、首尾相接。

for (i, x) in v.iter().enumerate() {
    println!("{i}: {x}");
}

let names = ["a", "b"];
let ages = [1, 2];
for (n, a) in names.iter().zip(ages.iter()) {
    println!("{n} = {a}");
}

let merged: Vec<i32> = [1, 2].into_iter()
    .chain([3, 4])
    .collect();

v.iter().take(2);    // 前两个
v.iter().skip(1);    // 跳过一个
v.iter().rev();      // 反转
v.windows(2);        // 相邻窗口
v.chunks(2);         // 分块
for 循环与所有权

iter() 借用、iter_mut() 可变借用、into_iter() 获取所有权。

let v = vec![String::from("a"), String::from("b")];

for s in v.iter() { }          // s: &String,v 仍可用
for s in &v { }                // 同上(语法糖)

let mut v2 = vec![1, 2];
for x in v2.iter_mut() { *x += 1; }

for s in v.into_iter() {       // s: String,循环后 v 被消费
    println!("{s}");
}

📄 文件与字符串处理

std::fs 读写文件

小文件用 read_to_string/write 一步到位。

use std::fs;

let content = fs::read_to_string("a.txt")?;
fs::write("b.txt", "hello")?;

let bytes = fs::read("a.bin")?;
fs::create_dir_all("out/logs")?;
fs::rename("old.txt", "new.txt")?;
fs::remove_file("tmp.txt")?;
fs::copy("src.txt", "dst.txt")?;
PathBuf 路径操作

Path 借用、PathBuf 有所有权,类似 str/String 关系。

use std::path::PathBuf;

let p = PathBuf::from("dir").join("sub").join("a.txt");
p.exists();
p.is_file();
p.is_dir();
p.file_name();      // Some("a.txt")
p.extension();      // Some("txt")
p.parent();         // dir/sub

for entry in fs::read_dir(".")? {
    println!("{}", entry?.path().display());
}
字符串方法与转换

split 返回迭代器;parse 需在 Result 上指定目标类型。

let s = "  Hello, Rust  ";
s.trim();
s.to_lowercase();
s.to_uppercase();
s.replace("Rust", "World");
s.contains("Hello");
s.starts_with("  ");
s.split(", ")
    .map(|x| x.trim())
    .collect::<Vec<_>>();

let n: i32 = "42".parse()?;
let s = 42.to_string();
let parts: Vec<&str> = "a,b,c".split(',').collect();
"2026".chars().count();       // 字符数(按 Unicode)
"abc".len();                  // 字节长度

📖 使用说明

全程在浏览器本地运行。

操作步骤:

  1. 搜索或浏览分类条目;
  2. 查看语法与示例代码;
  3. 点击复制代码块。

💬 用户评论 (0)

还没有评论,快来抢沙发!

请添加微信联系我