Rust 安装与环境配置
Rust 是目前最著名的 C++ 替代语言,它的版本管理和项目管理由两个组件提供:
rustup:Rust 工具链版本管理cargo:项目构建和包管理
对比 C++ 生态:rustup 管理编译器版本(C++ 没有标准的编译器版本管理器),cargo 融合了 CMake(构建)、vcpkg/Conan(包管理)和 CTest(测试)的功能。Rust 的标准工具链解决了 C++ 生态中长期以来需要拼凑不同工具才能覆盖的问题。
下面主要关注 Linux 环境下的使用,macOS 高度类似;Windows 中路径不同但概念一致。
Rust 工具链目录
rustup 管理的安装目录:
1 | ~/.rustup/ |
cargo 自身的数据存放在 ~/.cargo/:
1 | ~/.cargo/ |
C++ 对比:Rust 工具链相关文件集中在 ~/.rustup 和 ~/.cargo 中,标准卸载方式为 rustup self uninstall。C++ 环境中 GCC/Clang、CMake、vcpkg、clang-tidy 等工具分散在各处,没有统一的安装根目录。注意 Rust GNU 工具链仍可能依赖系统 linker、libc 等外部组件。
rustup 版本管理
C++ 生态中没有标准的编译器版本管理器——通常依赖系统包管理器(apt install g++-14)或 conda/spack 来安装特定编译器版本,切换版本比较繁琐。
rustup 填补了这个缺口,统一管理:
- 工具链安装和更新(
stable、beta、nightly通道) - 多版本切换(默认工具链、目录级覆盖)
- 附加组件管理(
rustfmt、clippy、rust-analyzer等) - 交叉编译 target 安装
安装与卸载
1 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh |
安装程序会自动把 ~/.cargo/bin 添加到 PATH 中。
验证:
1 | rustc --version |
卸载:
1 | rustup self uninstall |
通道与工具链管理
三个发布通道:
stable:生产使用beta:下一版 stable 的预览nightly:每日构建,包含实验性特性
1 | rustup toolchain install stable |
临时使用指定工具链执行命令:
1 | cargo +nightly build |
这在测试 nightly 特性或验证不同编译器版本的兼容性时很实用。C++ 中需要手动切换 CC/CXX 环境变量或修改 CMake 的 CMAKE_C_COMPILER。
组件管理
1 | rustup component list |
C++ 对比:
| 组件 | 作用 | C++ 近似等价 |
|---|---|---|
rustfmt |
代码格式化 | clang-format |
clippy |
静态检查/lint | clang-tidy |
rust-analyzer |
LSP 语言服务器 | clangd / ccls |
rust-docs |
本地标准库文档 | cppreference 离线版 |
rust-src |
标准库源码 | libstdc++/libc++ 源码 |
Rust 这些组件随工具链一起由 rustup 管理,统一安装和更新。C++ 中这些工具通常需要单独安装、各自维护版本兼容。
项目级版本固定
rust-toolchain.toml 用于为项目选择或固定开发工具链;它不同于 Cargo.toml 中声明最低支持 Rust 版本的 rust-version(类似 CMakeLists.txt 里的 cmake_minimum_required):
1 | [toolchain] |
放在项目根目录即可,rustup 会自动使用对应版本,未安装时会自动下载。
交叉编译 target
1 | rustup target list |
C++ 交叉编译需要手动安装对应平台的 toolchain(如 aarch64-linux-gnu-g++),配置 CMake toolchain file。Rust 通过 rustup target add 安装目标平台的 Rust 标准库;部分目标(如 aarch64-unknown-linux-gnu)仍需单独安装交叉链接器(如 gcc-aarch64-linux-gnu)并配置 linker。wasm32-unknown-unknown 这类不依赖系统链接器的 target 相对简单,但该便利性不能推广到所有 target。
cargo 项目管理和包依赖
cargo 相当于 CMake + vcpkg/Conan + CTest 的融合体,覆盖了构建、依赖管理和测试全流程。
创建项目
1 | cargo new --lib mylib # library package,默认包含 src/lib.rs |
标准项目骨架:
1 | myapp/ |
Cargo.toml
1 | [package] |
Cargo.lock 锁定依赖的精确版本。应用程序通常应提交以保证构建可复现;库项目是否提交取决于是否希望固定开发和 CI 环境——当前 Cargo 官方建议在不确定时提交。C++ 中 Conan 的 lockfile、vcpkg 的 baseline 与 overrides 概念类似,但使用远不如 cargo 统一。
构建和运行
1 | cargo build # debug 模式(类似 cmake -DCMAKE_BUILD_TYPE=Debug && make) |
构建产物位于项目的 target/ 目录(类似 CMake 的 build/):
1 | target/ |
cargo check是 Rust 的独特优势:跳过大多数 crate 的最终代码生成和链接,只做类型检查、trait 解析等,比完整编译快得多。注意 build script 仍需编译执行,proc macro 和部分 host dependency 仍需生成可执行代码。C++ 编译器也有仅做语法/语义检查的选项,但生态中缺少一个在项目、依赖和构建脚本层面完全对应的统一命令。日常开发中用cargo check替代cargo build可以显著缩短反馈周期。
跳过 cargo:直接使用 rustc
C++ 中常直接用 g++ -o hello hello.cpp && ./hello 快速试一小段代码。Rust 也能这样做——rustc 可以脱离 cargo 独自编译单个 .rs 文件:
1 | echo 'fn main() { println!("Hello, world!"); }' > hello.rs |
1 | # 启用优化 |
与 C++ 对比:
| 能力 | g++/clang++ | rustc |
|---|---|---|
| 单文件编译 | g++ foo.cpp -o foo |
rustc foo.rs |
| 指定输出名 | -o <name> |
-o <name> |
| 优化等级 | -O2 / -O3 |
-C opt-level=3 |
| 编译为库 | -shared / -c |
--crate-type lib/cdylib |
| 链接系统库 | -l<lib> |
-l <lib> |
| 查看标准库路径 | 无直接等价 | rustc --print target-libdir |
| 仅类型检查 | -fsyntax-only |
用 cargo check(rustc 无直接等价) |
不过对于大多数工作,仍然推荐使用 cargo——原因和 C++ 中不推荐长期用裸 g++ 命令而不用 CMake 类似:一旦涉及到第三方依赖、feature 配置、条件编译、测试和多 crate 组织,裸 rustc 命令会迅速变得不可维护。rustc 更适合快速实验、学习语言特性或 benchmark 微测试,正式项目则统一走 cargo。
依赖管理
crates.io 注册表:
1 | cargo add serde # 类似 vcpkg add / conan install |
Git 仓库依赖:
1 | cargo add --git https://github.com/user/repo.git |
C++ 对比:CMake 的 FetchContent(下载远程仓库并引用)或 Conan 的 Git 源。
本地路径依赖(开发中的库):
1 | cargo add --path ../mylib |
等价于 Cargo.toml 中:
1 | [dependencies] |
C++ 对比:CMake 的 add_subdirectory("../mylib") 或 find_package 配合 CMAKE_PREFIX_PATH。Rust 的做法更简单——一行声明即完成,不像 CMake 还需要在调用方加 target_link_libraries。
测试
1 | cargo test # 构建并运行所有测试(CTest) |
C++ 中测试框架选择和集成方式各不相同(GoogleTest + CTest、Catch2、Boost.Test 等),还需手动配置 CTest 或 CMake 的 add_test。Rust 中 cargo test 开箱即用,单元测试直接写在源文件中(#[cfg(test)] mod tests)。
其他常用命令
| 命令 | 作用 | C++ 近似等价 |
|---|---|---|
cargo check |
快速类型检查 | 无直接等价 |
cargo fix |
自动应用编译器建议 | clang-tidy --fix |
cargo fmt |
代码格式化 | clang-format -i |
cargo clippy |
静态检查/lint | clang-tidy |
cargo tree |
查看依赖树 | vcpkg depend-info / conan graph info / cmake --graphviz |
cargo doc |
生成文档 | doxygen |
cargo search |
搜索 crates.io | vcpkg search / conan search |
cargo publish |
发布到 crates.io | — |
cargo install |
安装二进制 crate | pip install / npm install -g |
Rust 包开发
package 与 crate
这两个概念在 Rust 中有严格区分,和 C++ 的对应关系并不直观。
crate 是 Rust 的编译单元。一次 rustc 调用处理一个 crate,根据 crate type 生成可执行程序、Rust 库(.rlib)、系统库(.so/.dll)或过程宏等产物。与 C++ 不同:C++ 的编译单元是单个 .cpp 文件(translation unit),通过链接合并为最终产物;crate 更接近最终产物而非中间的 .o。
crate 分为两种:
- binary crate:生成可执行文件,必须有
main函数。根模块默认为src/main.rs。 - library crate:生成库文件。默认输出
.rlib(Rust 自有的静态库格式,包含 Rust 元数据),根模块默认为src/lib.rs。可通过[lib]中的crate-type改变输出类型——例如cdylib生成 C ABI 兼容的.so/.dylib/.dll。
package 是 Cargo 的项目概念——即一个 Cargo.toml 所管理的目录。一个 package 可以包含多个 crate:
1 | myapp/ |
如果 src/ 下同时有 main.rs 和 lib.rs,这个 package 就包含一个 library crate 和一个 binary crate。此外,src/bin/ 下的每个 .rs 文件也都是独立的 binary crate(如 src/bin/tool.rs 编译为可执行文件 tool)。
C++ 对比:C++ 中构建产物类型(可执行文件 vs 静态/动态库)和编译单元(.cpp)是分离的概念,由 CMake 的 add_executable / add_library 分别管理。Rust 将”编译范围 + 产物类型”统一到 crate 概念中,由入口文件位置(main.rs / lib.rs)和 Cargo.toml 共同决定。
模块系统
Rust 以 crate 为根,通过 mod 声明构建模块树。这与 C++ 的 #include 有本质区别。
C++ 的 #include 是文本级复制——预处理期将头文件内容逐字插入源文件,声明和定义分离在 .h 和 .cpp 中。Rust 没有头文件,mod 声明不复制文本,而是将对应文件/目录作为模块挂载到 crate 的模块树中:
1 | src/ |
1 | // 在 main.rs 或 lib.rs 中: |
几个 C++ 程序员需要适应的关键差异:
- 没有头文件,没有声明/定义分离。Rust 没有 C++ 式的头文件与源文件分离。普通函数的签名和函数体构成同一个定义,公共接口通过
pub和模块可见性控制。trait 方法声明与对应的impl可以位于不同模块或文件中。 - 模块路径不是文件路径。
mod foo中的foo是模块名而非文件名;文件只是模块的载体。模块路径是 crate 内部的逻辑路径,不等同于文件路径;但使用文件承载外部模块时,默认模块解析仍遵循规定的目录布局(foo.rs、foo/mod.rs、foo/bar.rs等)。 - 不需要
#include防护或#pragma once。模块天然不会重复引入。 use相当于引入路径别名,不影响编译产物的包含关系(mod决定哪些代码参与编译,use只是缩短路径)。C++ 中#include和using是独立的;Rust 中mod和use也是独立的,但mod没有 C++ 的直接等价物。
Rust 2018+ 中,
foo.rs和foo/mod.rs二者只能选其一,不能同时存在。foo.rs内部可以直接声明pub mod bar;来挂载foo/bar.rs,无需降级到foo/mod.rs。社区一般偏好前者(少一个mod.rs嵌套)。
workspace(多包仓库)
对于大型项目,workspace 在一个仓库中管理多个 package(类似 CMake 的 add_subdirectory 或在顶层 CMakeLists.txt 中批量管理子项目):
1 | my-workspace/ |
workspace 根 Cargo.toml:
1 | [workspace] |
C++ 对比:CMake 父子项目各自独立管理依赖版本,容易产生不一致。Rust workspace 共享 Cargo.lock 和 target/,各成员使用同一个依赖解析结果,但 Cargo 仍可能在同一依赖树中同时存在多个不兼容版本(使用 cargo tree -d 可查看重复版本)。
features(条件编译)
Rust 的 features 对标 C++ 预处理器(#ifdef / #if defined(...) 配合 CMake 的 option()),但集成度更高:
1 | [features] |
1 | cargo build --features "async" |
代码中通过 #[cfg(feature = "async")] 控制编译路径。与 #ifdef 的区别:features 在 Cargo.toml 中显式声明,可组合其他 features 或激活可选依赖——例如 async = ["dep:tokio"] 显式规定启用 async 时同时启用可选依赖 tokio。相比之下,C++ 预处理宏本身只是自由的文本条件。
发布到 crates.io
1 | cargo login <token> |
crates.io 注册表
crates.io 是 Rust 的中心化包注册表,类似 vcpkg 的 ports 目录或 Conan Center,但有统一的 CLI 入口和版本策略。
cargo add serde 幕后的流程:
- 在
~/.cargo/registry/中查找本地缓存的索引 - 根据
Cargo.toml中声明的版本约束解析依赖——Rust 默认使用语义化版本(^0.8表示>=0.8.0, <0.9.0) - 确定完整依赖树,更新
Cargo.lock - 从 crates.io 下载 crate 源码到
~/.cargo/registry/src/ - 构建依赖,产物放在项目的
target/目录
镜像源配置: ~/.cargo/config.toml 中指定:
1 | [source.crates-io] |
cargo install — 全局二进制工具
1 | cargo install ripgrep # 类似 brew install / apt install |
安装到 ~/.cargo/bin/ 中,确保其位于 PATH 下即可使用。C++ 生态中没有统一的直接对应命令,通常使用系统包管理器、vcpkg、Conan,或者手动编译安装。
Rust FFI:与 C/C++ 互操作
对 C++ 背景的用户来说,Rust FFI(Foreign Function Interface)是关键需求。Rust 通过 C ABI 作为中间层与外部语言互操作。
调用 C 函数
1 | unsafe extern "C" { |
如需链接额外的原生库,可在 build.rs 中输出 Cargo instruction,例如 println!("cargo::rustc-link-lib=m");;若项目包含需要一同编译的 C/C++ 源码,则可使用 cc crate。在 Rust 2024 中,extern "C" block 必须标记为 unsafe,其中未显式标记 safe 的外部函数默认是 unsafe 调用。
将 Rust 编译为 C 可链接的动态库
1 | // lib.rs |
1 | # Cargo.toml |
生成的动态库在 Linux、macOS 和 Windows 上分别为 .so、.dylib 和 .dll,可分别通过 dlopen 或 LoadLibrary 动态加载,也可由 C/C++ 程序直接链接(配合 cbindgen 生成头文件)。C++ 程序调用 Rust 库的路径与调用 C 库一致。
常用 FFI 生态工具
| 工具 | 作用 | C++ 相关场景 |
|---|---|---|
bindgen |
从 C 头文件生成 Rust FFI 绑定 | 复用现有 C/C++ 库 |
cbindgen |
从 Rust 代码生成 C 头文件 | 将 Rust 库暴露给 C++ 项目 |
cc crate |
在 build.rs 中编译 C/C++ 代码 |
混编 C/C++ 和 Rust |
cxx crate |
安全的 C++ ↔ Rust 互操作 | 直接对接 C++ 类型(std::string 等) |
pyo3 + maturin |
Rust → Python 扩展模块 | 用 Rust 替代 C++ 写 Python 扩展 |
wasm-bindgen |
Rust → WebAssembly/JavaScript | 将计算密集逻辑从 C++(Emscripten)迁移到 Rust |
缓存与磁盘管理
1 | du -sh target/ # 项目构建产物(删除后不影响其他项目) |
cargo clean:清理当前项目target/,类似删除 CMakebuild/目录- 删除
~/.cargo/registry/、~/.cargo/git/:下一次cargo build会重新下载 - C++ 对比:vcpkg 的二进制缓存通常位于
~/.cache/vcpkg/archives/,Conan 2 的本地缓存默认位于~/.conan2/,概念类似
推荐工作流
新项目:
1 | cargo new myapp && cd myapp |
已有项目:
1 | git clone <url> && cd project |
固定工具链版本:
1 | # rust-toolchain.toml |
Rust 与 C++ 对比
| 关注点 | Rust 方案 | C++ 生态中的近似等价 |
|---|---|---|
| 编译器版本管理 | rustup |
无标准方案(系统包管理器 / conda / spack) |
| 构建系统 | cargo build |
CMake + Make/Ninja |
| 包管理 | cargo add / crates.io |
vcpkg / Conan |
| 项目配置 | Cargo.toml |
CMakeLists.txt + vcpkg.json |
| 锁文件 | Cargo.lock |
Conan lockfile / vcpkg baseline 与 overrides |
| 测试 | cargo test |
CTest + GoogleTest / Catch2 |
| 格式化 | cargo fmt |
clang-format |
| 静态检查 | cargo clippy |
clang-tidy |
| 文档 | cargo doc |
Doxygen |
| 条件编译 | features + #[cfg] |
#ifdef + CMake option() |
| 构建产物目录 | target/ |
build/ |
Rust 的工具链设计将 C++ 生态中分散的工具整合为一套统一方案,从编译器安装、构建、依赖管理到测试和发布都有标准的 CLI 入口。这种一致性是 Rust 工程体验的重要特征。C++20 modules 主要改进代码组织和编译模型,并未统一编译器管理、构建、依赖管理和测试工具。
