Rust 命令速查表 - Rust 与 Cargo 常用命令大全
这套速查给写 Rust 的开发者,尤其围绕每天都要碰的 Cargo 工具链:项目脚手架、debug 与 release 两态编译、跑测试、生成文档、用 Clippy 扫 lint、用 rustfmt 统一格式、按目标平台交叉编译。与逐个罗列 cargo 子命令不同,条目沿"编辑→编译/检查→测试→lint→格式化→发布"的真实节奏组织。读完应能把一个新 crate 从 cargo new 一路带到 green 的 Clippy+测试和可交付的 release 构建。
典型使用场景
面向用 Rust 写服务、CLI 或并发 worker 的开发者。Rust 的学习曲线主要卡在所有权与借用检查,本表把两块分开处理:一块是 cargo 命令行(项目管理、依赖、测试、检查、发布、交叉编译),另一块是语法核心(所有权、借用、生命周期省略、Result 与 ? 传播)。高频写代码时用 cargo check 做快速类型验证比 cargo build 快得多,这是最值得先改掉的一个习惯。
项目管理 9
cargo new project_namecargo new --lib project_namecargo initcargo buildcargo build --releasecargo runcargo run --releasecargo checkcargo clean依赖管理 6
cargo add serdecargo add serde --features derivecargo remove serdecargo updatecargo treecargo tree --invert测试与文档 5
cargo testcargo test -- --nocapturecargo test test_namecargo doc --opencargo doc --no-deps代码质量 4
cargo clippycargo clippy --fixcargo fmtcargo fmt -- --check发布与跨平台 6
cargo publishcargo publish --dry-runcargo build --target x86_64-unknown-linux-muslrustup target listrustup target add x86_64-pc-windows-gnucargo install cargo-edit语法核心 14
let s2 = s1;let r = &s;let r = &mut s;fn foo<'a>(x: &'a str) -> &'a strfn foo(x: &str) -> &strmatch x { Some(v) => v, None => 0 }if let Some(v) = opt {}while let Some(v) = iter.next() {}let Ok(v) = res else { return; };Result<T, E>Option<T>fn foo() -> Result<T, E>result?#[derive(Debug, Clone, PartialEq)]模块系统 7
use std::collections::HashMap;use std::io::{self, Read};mod my_module;pub fn foo() {}pub(crate) fn internal()pub use crate::utils::helper;crate::module::func()参数矩阵
| 参数 | 作用 | 示例 |
|---|---|---|
cargo new --lib | 创建库型项目,入口 src/lib.rs | cargo new --lib mylib |
cargo check | 只做类型检查不生成二进制,最快 | cargo check |
cargo build --release | 优化构建,产物在 target/release | cargo build --release |
cargo add serde --features derive | 加依赖并开启 feature | cargo add serde --features derive |
cargo update | 按约束升级 lock 中的依赖 | cargo update |
cargo tree --invert | 反查哪些 crate 依赖了某包 | cargo tree --invert serde |
cargo clippy --fix | 运行 lint 并自动修复 | cargo clippy --fix |
cargo fmt -- --check | 格式检查,CI 门禁用 | cargo fmt -- --check |
result? | 错误传播:出错即提前返回 | let v = parse(s)?; |
&s / &mut s | 不可变 / 可变借用 | fn f(s: &mut String) |
#[derive(Debug, Clone)] | 自动派生 trait 实现 | #[derive(Debug, Clone, PartialEq)] |
rustup target add <triple> | 为交叉编译添加目标平台 | rustup target add x86_64-pc-windows-gnu |
易错点与避坑指南
现象编译报 cannot move out of ... ,值在使用后又被引用。
原因赋值/传参默认是所有权转移(move),原变量失效。
处置改用借用 &s 传参;确需持有两份时用 s.clone()。
现象同一作用域内同时可变借用了两次,编译器报 borrow conflict。
原因Rust 在任意时刻只允许一个 &mut 借用。
处置把两次借用拆到不同作用域,或改用索引/拆分数据结构避免重叠生命周期。
现象函数返回引用时报 missing lifetime specifier。
原因返回值引用了参数时,编译器无法推断生命周期关系。
处置显式标注 fn foo<'a>(x: &'a str) -> &'a str,或让函数直接接收并返回 String 避免引用。
现象cargo build 突然变慢或报奇怪的编译错误,重编后又好了。
原因target 目录里的增量编译缓存与当前源码状态不一致。
处置cargo clean 清空 target 后重编。
现象升级依赖后编译失败,锁文件已自动被改动。
原因cargo update 把 Cargo.lock 里的次要依赖一并升级。
处置升级前备份 lock;用 cargo update -p <crate> --precise <ver> 精确控制单个依赖版本。
现象交叉编译报错,缺少链接器或找不到 target。
原因未安装对应平台目标,或缺少该平台的链接工具链。
处置rustup target add <triple> 安装目标,并确认所需的交叉链接器已就位。
排障路径
1先用最快的检查确认能否编译
cargo check不生成二进制,最快验证类型与借用检查;日常改代码首选。
2定位是谁引入了某个依赖
cargo tree --invert serde反向查询连锁升级的影响面,决定能否移除。
3缓存出问题时的彻底重建
cargo clean && cargo build清空 target 目录;只在怀疑缓存不一致时用,会全量重编。
4发布前先模拟一次
cargo publish --dry-run不真正发布,验证打包与元数据是否符合 crates.io 要求。
5CI 里的质量门禁
cargo fmt -- --check && cargo clippy格式与 lint 都过再构建,是最小可用的 CI 检查组合。
命令示例
加依赖并顺手开 feature,不要手改 Cargo.toml
cargo add serde --features derivederive feature 决定能否写 #[derive(Serialize)]。用 cargo add 还会自动选一个与你当前工具链兼容的版本号并写进 Cargo.toml,手改清单则容易留下一个解析不了的版本约束。
输出
Adding serde v1.0.210 to dependencies
Adding serde_derive v1.0.210 to dependencies升级某个包前先看它被谁依赖
cargo tree --invert serde反向依赖树回答的是「谁依赖了我」,正向 cargo tree 回答的是「我依赖了谁」。评估一次可能引发连锁升级的改动(尤其是加 feature)时,先 --invert 看清影响面,再决定要不要动。
输出
my-app v0.1.0 └── serde v1.0.210
调试时让 println 真的打印出来
cargo test -- --nocapture测试默认捕获 stdout,通过的测试不会打印任何东西,于是「我明明写了 println 却没看到」成为最常见的困惑。-- --nocapture 把参数透传给测试二进制,关掉捕获。中间那对 -- 是必须的:cargo 之后、交给测试运行时的参数之前。
CI 里用 fmt --check 把格式卡死
cargo fmt -- --check不加 -- --check 时 cargo fmt 会直接改写文件;加了它就变成只检查、有差异就非零退出。CI 里用 check 版本,提交前本地跑 cargo fmt 真正落盘,两者配对使用。
输出
Diff in /src/main.rs at line 14: - let x=1; + let x = 1;
let-else 把「不匹配就退出」写在一行
let Ok(value) = config.get("port") else {
return;
};表里的 match / if let / while let / let-else 是同一套模式的四种写法。let-else 的特点是必须 else 分支发散(return、break、panic),编译器才能把它收窄成后续可用的绑定,避免了 Option 层层解包。
常见坑与注意事项
- cargo clean 会删掉整个 target 目录,一次性丢掉全部增量编译缓存,几分钟的编译重来;只在增量编译结果确实错乱时才用。
- Cargo.lock 必须提交进版本库:库项目也应提交,否则不同机器解析出的传递依赖版本不同,问题无法复现。
- cargo update 会在满足约束的前提下把锁文件里的版本推到最新,diff 可能很大;上线前后的依赖升级要单独成一次提交,出问题才好二分定位。
- cargo publish --dry-run 只跳过真正的上传,打包、校验与依赖图检查都会真跑,但版本号一旦被占用就无法复用,发布前务必先 dry-run。
- 交叉编译只负责产出目标平台的代码,链接阶段仍需要对应工具链(如 Linux musl 目标的 linker);缺工具链时报错发生在链接而不是编译,容易误判成源码问题。
- cargo clippy --fix 会批量改写源码,改动常常超出预期范围;必须先看 clippy 的报告、再在干净的提交上让它改,并复核 diff。
提示
- cargo check 比 cargo build 快很多,日常开发用它检查编译错误。
- cargo clippy 是官方 lint 工具,建议加入 CI 流程。
- 发布前务必运行 cargo publish --dry-run 验证。
- 同一时间只能有一个可变引用或多个不可变引用,二者不可共存。
- ? 运算符只能在返回 Result 的函数中使用,自动将 Err 向上传播。
- 生命周期省略规则适用于常见模式,复杂场景需显式标注 lifetime 参数。
常见问题
cargo check 和 cargo build 什么时候用哪个?
日常改代码时用 cargo check——它只做类型与借用检查,不生成二进制,比 cargo build 快很多,适合频繁小步改动。确认没问题后 cargo build 产出 debug 版本(在 target/debug,不开优化,编译快),上线前用 cargo build --release 拿到 target/release 的优化产物。想直接看运行效果用 cargo run,加 --release 可以验证优化后的真实性能。遇到莫名其妙的增量编译错误,最后再用 cargo clean 清空 target 重来。
报同时存在两个可变引用,怎么改?
Rust 的核心规则是同一时刻只能有一个 &mut 引用,或者任意多个 & 不可变引用,二者不能共存。典型冲突是两个作用域各自持有 &mut s,比如分别 &mut vec[0] 和 &mut vec[1]。解决办法是先用不可变借用把需要的值取出来、结束借用再进入可变借用;或者拆分借用,对不同字段用不同变量访问;实在不行就 let s2 = s1; 直接走所有权转移,把值交出去。
什么时候该返回 Result,什么时候用 Option?
判断标准是「缺失」还是「失败」。Option<T> 表达可空,用 Some/None 包装,出现在「可能没有值」的场景,比如字典查找。Result<T, E> 表达成功或某种错误,适合返回 fn foo() -> Result<T, E>,调用方用 result? 运算符把 Err 提前向上传播——注意 ? 只能用在返回 Result 的函数里,它会把错误自动转换并 return。想简化一次性匹配用 if let Some(v) = opt {} 或 let Ok(v) = res else { return; };。
加依赖时 cargo add 和手改 Cargo.toml 有什么区别?
cargo add serde 会自动选取最新兼容版本写入 Cargo.toml 并让代码里立刻能 use,省去手工查版本号。带功能时用 cargo add serde --features derive,顺便打开派生宏支持,配合 #[derive(Debug, Clone, PartialEq)] 这类实现。删除用 cargo remove serde,它同时清理不再需要的引用清单。想在现有约束内升级已有依赖用 cargo update,它会更新 Cargo.lock 里的版本而不改清单。
升级一个依赖前,怎么先摸清它会影响到谁?
先看正向依赖树:cargo tree 以树形打印所有依赖关系,能直观看到传递依赖和版本来源。要评估某个具体包的升级影响面,用反向查询 cargo tree --invert,它列出到底哪些 crate 依赖着目标包——如果影响面比预期大,就该先降级发布再升级。下游如果担心 API 破坏影响代码,升级后依次跑 cargo check、cargo clippy、cargo fmt -- --check 和 cargo test 确认没退化。
由 巧匠 维护
公开更新于 2026年7月21日,内容持续校对官方文档。