你刚把 Rust 装好,编辑器也认出了 .rs 文件,于是信心满满地写下第一行代码。几秒钟后,终端里出现一大片英文,最后还跟着一句 aborting due to previous error。很多人的第一反应是:我是不是连安装都装错了?
先别急着重装。Rust 的入门门槛确实比直接运行一段 Python 或 JavaScript 代码高一点,因为它把工具链、编译、链接和项目构建这些环节都摆在了你面前。但这也有一个好处:只要我们把这条链路走通一次,后面遇到问题时就能很快判断,到底是环境没配好、代码没通过检查,还是项目根本不在正确的目录里。
这一章不急着讲所有权,也不拿“内存安全”之类的大词劝你坚持。我们先做一件更实际的事:从一台还不能写 Rust 的电脑出发,安装工具链,写出第一个程序,再把它交给 Cargo 管理。等你读完,至少应该能独立回答下面这些问题:
rustup、rustc 和 cargo 各自负责什么?cargo check、cargo build 和 cargo run 有什么差别?target/debug 与 target/release 里的程序为什么不是同一种构建?Cargo.toml”时,应该先查哪里?我们会把每一步都实际跑一遍。你不必一次记住所有命令,先建立一张清楚的地图,比背命令更有用。

第一次接触 Rust 时,rustup、rustc、Cargo 这几个名字很容易挤成一团。你可以把它们想成一个小型工作组。
rustc 是真正读代码的编译器。你把 .rs 源文件交给它,它会检查语法和类型,生成目标代码,再配合系统的链接器得到可执行文件。如果你的代码违反了 Rust 的规则,通常也是它第一个站出来拦你。
Cargo 是项目管家。它知道源代码放在哪里、项目使用哪些依赖、应该用什么配置编译,以及构建结果放到哪里。你执行 cargo build 时,最后干编译活的仍然是 rustc,只是 Cargo 帮你组织了参数、文件和依赖。小到一个练习,大到几十个包组成的工作区,这套入口基本不变。
rustup 管的则是工具链。这里的“工具链”不是单独一个编译器,而是一组可以配合工作的工具,包括某个版本的 rustc、与之匹配的标准库、Cargo,以及按需安装的文档、格式化器等组件。Rust 有 stable、beta 和 nightly 等发布通道,也可以安装指定历史版本。对刚开始学习的人来说,默认的 stable 足够,而且最省心。
把关系压缩成一句话就是:你用 rustup 安装和切换工具链,用 Cargo 管项目,Cargo 再调用 rustc 编译代码。

只安装某个编译器当然不是理论上做不到,但你很快就会遇到版本升级、附加组件、交叉编译目标和多个项目需要不同版本的问题。自己手动维护这些东西,就像把工具箱里的每一把螺丝刀都分别登记,能做,却很容易把时间花在不该花的地方。
rustup 解决的是“这台机器现在有哪些 Rust 工具链,以及当前项目该用哪一套”。以后你可能会看到这样的命令:
rustup toolchain list
rustup default stable
rustup toolchain install nightly第一条列出已经安装的工具链,第二条把稳定通道设为默认工具链,第三条额外安装 nightly。现在不用急着装 nightly。许多实验性功能只能在 nightly 上用,但学习基础语法和编写普通项目并不需要它。多装一套工具链还会多占磁盘空间,并带来“为什么同一段代码在两个终端里表现不同”的排查成本。
如果你没有明确理由选 beta、nightly 或某个固定版本,就使用 stable。版本选择并不是越新奇越专业;能让团队和教程稳定复现,往往才是更好的选择。
有些终端教程会在命令前写 $,PowerShell 示例则可能写 >。它们通常只是终端提示符,不是命令的一部分。为了让命令可以直接复制,本章的代码块不带提示符。例如看到:
rustc --version你只输入 rustc --version,不要自己再补一个 $。
安装最容易让人紧张的地方,不是命令有多复杂,而是不同系统会在链接器和环境变量上表现得不一样。我们分开处理,不把三个平台硬塞进同一个步骤里。
打开 Rust 的官方安装页面,复制页面当前提供的 Linux/macOS 安装命令,在终端中执行。安装命令会下载并启动 rustup 安装程序。交互界面通常会提供默认安装选项;如果你只是准备学习,接受默认配置即可。默认方案会安装当前稳定工具链,并把常用命令放到 Cargo 的可执行文件目录。
安装完成后,先关闭当前终端并重新打开一个。这个动作看起来朴素,实际很有用:新终端会重新读取 shell 配置和 PATH。如果你不想重开终端,也可以按安装程序最后给出的提示加载环境脚本,但不同 shell 的写法可能不同,照着你终端里实际显示的命令执行即可。
Rust 编译出的程序最终还要经过系统链接器。大多数开发环境已经有它,但一台很干净的新机器未必有。macOS 可以先安装命令行开发工具:
xcode-select --installLinux 则需要按发行版安装 GCC 或 Clang 一类的本地编译工具。以 Ubuntu 为例,常见做法是安装 build-essential:
sudo apt update
sudo apt install build-essential这不是说 Rust 编译器内部依赖 C 才能理解 Rust 代码,而是生成本机可执行文件时通常需要系统链接器;另外,一些日后会用到的第三方库也可能包含 C 代码。
Windows 上使用官方提供的 rustup-init.exe 安装器。下载与系统架构匹配的安装器并运行后,它会在控制台里引导你完成安装。对大多数现代 Windows 开发环境,默认使用 MSVC 工具链。
这里常见的坑是:rustup 装好了,rustc --version 也能执行,但一编译就提示缺少 link.exe,或者找不到某些 Windows SDK 库。原因通常不是 Rust 源代码,而是 MSVC 的本机构建前置项没有安装完整。安装 Visual Studio 的 C++ 构建工具时,要确保包含桌面 C++ 工作负载和相应的 Windows SDK。
安装完成后重新打开 PowerShell 或 Windows Terminal。旧窗口仍然保留安装前的环境变量,继续在里面敲命令,很容易让你误以为安装失败。
在常见默认配置下,Rust 的代理命令会放到用户目录下的 .cargo/bin 中。类 Unix 系统通常对应用户主目录下的 .cargo/bin,Windows 通常对应用户配置目录下的 .cargo\bin。安装程序会尝试把这个目录加入 PATH。
PATH 可以理解成终端的“命令查找清单”。你输入 cargo 时,终端不会扫描整块硬盘,而是依次去 PATH 列出的目录寻找名为 cargo 的可执行文件。目录没有被加入清单,文件即使真实存在,终端也会说找不到命令。
不要一看到“command not found”就反复运行安装器。先开一个新终端,再检查 PATH。重复安装通常不会让旧终端自动刷新,反而可能让你分不清机器上到底有几套 Rust。

普通开发使用的默认配置通常会包含编译器、标准库、Cargo、本地文档、rustfmt 和 Clippy 等常用组件。我们可以查看当前工具链已经安装的组件:
rustup component list --installed其中 rustfmt 用来统一代码格式,Clippy 会给出比基础编译检查更偏向代码习惯和潜在问题的建议。它们很有用,但本章先不展开。你现在只需要知道,rustup 管的不只是 rustc 一个文件。
Rust 也提供不同的安装 profile。minimal 只保留能工作的核心组件,适合对体积敏感的自动化环境;default 更适合日常开发;把所有组件一股脑装齐通常没有必要。新手使用默认 profile,能少掉不少“为什么本地文档和格式化器不见了”的疑问。
以后想更新 rustup 管理的工具链,可以执行:
rustup update想打开随工具链安装的本地文档,可以执行:
rustup doc这在网络不稳定时尤其方便。标准库里的类型和函数记不清,不必靠猜,打开文档查一下通常更快。
如果你确实决定完全移除由 rustup 安装的 Rust,可以执行:
rustup self uninstall卸载会移除 rustup 管理的工具链和相关数据,不要把它当成修复单个项目编译错误的常规操作。一个括号少写了,和整套工具链需要被铲掉重装,通常隔着十万八千里。
现在先不碰编辑器。我们在一个新终端里依次执行几条只读命令,把工具链的状态确认清楚:
rustup --version
rustc --version
cargo --version
rustup show active-toolchain前三条应该分别显示 rustup、Rust 编译器和 Cargo 的版本信息。具体版本号、提交标识和日期会随安装时间变化,所以不要拿你的输出和某张旧截图逐字对照。重点是每条命令都能正常执行,并返回版本,而不是报“找不到命令”。
最后一条告诉你当前活动工具链。初次安装后的常见结果会包含 stable 和本机的目标三元组。目标三元组描述编译器面向的架构、操作系统和相关环境,例如你是在 64 位 Linux、Apple 芯片 macOS,还是 Windows MSVC 环境上工作。输出不必和别人的机器相同,只要它与你的系统匹配。
这种情况通常说明 rustup 本身可执行,但还没有可用的默认工具链。先查看已安装列表:
rustup toolchain list如果列表里没有 stable,可以安装并设为默认:
rustup toolchain install stable
rustup default stable然后重新执行:
rustc --version
cargo --version如果工具链明明已经存在,当前目录里却使用了另一套版本,还可以用 rustup show 看更完整的选择过程。Rust 项目可以通过目录覆盖或 rust-toolchain.toml 固定工具链,因此“全局默认是 stable”不保证每个目录都一定使用同一套版本。刚开始学习时你多半不会主动创建这些配置,但从别人那里拿来的项目可能会带着它们。
先确认你是否打开了安装之后的新终端。若问题仍在,检查 PATH。
Linux 或 macOS:
echo $PATHPowerShell:
$env:PathWindows CMD:
echo %PATH%你需要在输出中找到 Cargo 的 bin 目录。若目录缺失,应修正当前 shell 或系统的环境变量配置,而不是随手把不明路径复制进去。还要留意同一台电脑是否早就通过系统包管理器装过另一套 Rust。下面这些命令能告诉你终端实际找到了哪个程序:
which rustc
which cargoPowerShell 可以使用:
Get-Command rustc
Get-Command cargo如果路径指向意料之外的旧安装,问题就不是“Rust 没装”,而是 PATH 的先后顺序让旧命令抢在了 rustup 代理前面。先认清路径再调整,别同时维护两套自己都说不清来源的工具链。
重新打开终端,先运行 rustup --version。这一步只确认 rustup 命令是否能被终端找到。
运行 rustup show active-toolchain,确认存在当前可用的工具链。如果没有,就安装并选择 stable。
分别运行 rustc --version 与 cargo --version。两者都成功,才说明编译器和项目工具已经就位。
真正编译一个最小程序。版本命令成功只能证明工具存在,编译成功才能把系统链接器也一起验证掉。
最后一步很关键。环境体检像检查厨房里有没有锅,真正编译才是在确认燃气也能点着。
下面的互动面板把安装故障按症状拆开了。你可以选择当前看到的提示,再对照应该检查 PATH、工具链还是系统链接器。
我们先不用 Cargo,直接把一个源文件交给 rustc。这样你能看清最短的编译路径,以后使用 Cargo 时也知道它在替你做什么。
找一个专门放练习的目录,不要在下载目录或某个大型项目深处随手创建文件。目录名使用简单的英文和下划线,能避开部分终端和旧工具对空格、特殊字符处理不一致的问题。
Linux、macOS 或 PowerShell 可以这样创建目录:
mkdir rust_playground
cd rust_playground
mkdir first_program
cd first_program在这个目录中新建 main.rs,先故意写成下面这样:
fn main() {
println("编译器,请检查这段代码");
}然后编译:
rustc main.rs它不会通过。编译器会把注意力指向 println 附近,并提醒你这里应该调用宏。问题就在少掉的感叹号:Rust 中的 println! 是宏,不是名为 println 的普通函数。
这类报错一开始看着有点凶,但你可以先只读三处:错误编号或错误类别、带箭头的源代码位置、编译器给出的修改建议。不要从第一行一路逐字翻译到最后一行,那会让真正有用的信息淹没在细节里。
把代码修正为:
fn main() {
println!("编译器,请检查这段代码");
}再次编译:
rustc main.rs如果命令安静地结束,并回到终端提示符,通常就是成功了。很多命令行工具遵循“成功时少说话,出错时给信息”的习惯,所以没有出现庆祝动画并不代表它偷懒。
fn 表示我们要定义函数,main 是可执行程序的入口名称。程序启动后,会从这个函数开始执行。
() 是参数列表。这里为空,表示这个 main 没有接收普通函数参数。花括号 {} 围出函数体,真正要执行的语句放在里面。
println! 会打印一行文字并在末尾换行。感叹号说明它是宏调用;宏可以接收更灵活的语法并在编译期间展开。现在不用追进宏系统的底层,只要别把 ! 当成装饰符号丢掉。
双引号包住的是字符串字面量,末尾的分号表示这条语句在这里结束。Rust 并不是每一行都机械要求分号,后面你会遇到不带分号的表达式;但在这个位置,写分号是正确且自然的。
Rust 社区习惯使用四个空格缩进。编译器对这里的空格不敏感,但统一格式能让代码更好读。以后在 Cargo 项目里,可以让格式化工具处理这些排版,不必把宝贵的注意力花在数空格上。
rustc main.rs 只负责把源文件编译成当前平台的可执行文件,它没有自动运行你的程序。
在 Linux 和 macOS 上运行:
./main在 PowerShell 上运行:
.\main.exe你应该看到:
编译器,请检查这段代码这里的 ./ 或 .\ 很重要。它告诉终端“运行当前目录里的文件”。出于安全和命令查找规则,当前目录通常不会被当成全局命令目录。只输入 main,终端未必会替你猜到要运行当前文件夹中的程序。
Rust 是提前编译语言。源代码先被编译成适合目标平台的机器程序,然后再运行。这样做的代价是开发链路里多了一个明确的编译步骤;好处是运行时不需要再逐行解释源代码。生成的本机可执行文件通常也不要求目标机器安装 Rust 工具链,不过它仍然要符合目标操作系统、处理器架构和动态库环境。把 macOS 上生成的 main 直接复制到 Windows,并不会因为它是 Rust 写的就突然跨平台。

为了确认你运行的确实是刚改过的文件,而不是某个旧程序,把 main.rs 改成:
fn main() {
let learner = "你";
let tool = "rustc";
println!("{learner}已经让{tool}完成了第一次编译。");
}重新执行编译和运行:
rustc main.rs
./mainWindows 最后一条改为:
.\main.exe输出应为:
你已经让rustc完成了第一次编译。如果输出仍然是上一版文字,最常见的原因是只运行了旧可执行文件,却忘了重新编译。编辑 .rs 文件不会自动改写已经生成的程序。这个坑很朴素,也很常见,尤其是你在编辑器和终端之间来回切换的时候。
直接调用 rustc 很适合学习编译过程、验证一个单文件片段,或者理解某个底层参数。但真正的项目通常有多个源文件、第三方依赖、不同构建配置和测试。你当然可以手工拼出所有编译参数,只是这会迅速变成一项没有多少学习价值的体力活。
所以我们学习 rustc,不是因为以后每个项目都要手敲它,而是为了看清地基。接下来交给 Cargo 时,你会知道 Cargo 是在组织编译,不是在施展某种看不见的魔法。
下面的工作台把“修改源文件、重新编译、运行可执行文件”三个动作摆在同一条时间线上。你可以用它检查自己是否把编译失败和运行失败混在了一起。
进入真实开发后,你最常使用的入口会变成 Cargo。它既是构建工具,也是包管理器。项目需要第三方库时,Cargo 负责解析和下载依赖;项目只包含标准库时,它仍然负责目录约定、编译参数、增量构建和产物位置。
先退回到你准备用来存放项目的目录,然后创建一个新项目:
cd ..
cargo new rust_station
cd rust_stationcargo new rust_station 会创建名为 rust_station 的目录,并在里面生成一个可运行的二进制项目。--bin 是这类项目的默认值,所以这两种写法效果相同:
cargo new rust_station
cargo new rust_station --bin第二条不要在第一条之后接着执行,因为目录已经存在。这里把两种写法放在一起,只是为了说明 --bin 的含义。
如果你不希望 Cargo 初始化版本控制信息,可以在创建时指定:
cargo new rust_station --vcs none如果目录和一些文件已经存在,要把它变成 Cargo 项目,通常使用 cargo init,而不是再执行 cargo new:
cd existing_directory
cargo initnew 偏向“从零创建目录和项目”,init 偏向“在现有目录里补齐 Cargo 项目结构”。它们长得像,使用场景却不一样。
刚生成的二进制项目核心结构通常是:
rust_station/
├── Cargo.toml
└── src/
└── main.rs如果 Cargo 初始化了 Git,你还会看到版本控制相关文件。完成第一次构建后,项目根目录通常还会出现 Cargo.lock 和 target 目录:
rust_station/
├── Cargo.lock
├── Cargo.toml
├── src/
│ └── main.rs
└── target/
└── debug/
src/main.rs 是二进制程序的入口源文件。Cargo 知道去 src 下找代码,因此你不用每次都把文件路径交给 rustc。
Cargo.toml 是项目清单。它使用 TOML 格式,保存包名、版本、Rust edition 和依赖声明。新项目里的内容大致如下:
[package]
name = "rust_station"
version = "0.1.0"
edition = "2024"
[dependencies][package] 下是这个包的基本信息。edition 表示这个项目按哪个 Rust edition 的规则解释源码。edition 不是编译器版本号:同一套现代编译器可以支持多个 edition,项目也不会因为更新工具链就被强制改写成最新 edition。当前 cargo new 默认创建 2024 edition 项目;如果你看到旧项目使用 2021,这不等于项目损坏。
[dependencies] 是日后声明第三方 crate 的地方。crate 是 Rust 编译和复用代码时使用的基本单元。现在项目没有外部依赖,这一节暂时为空。
Cargo.lock 记录依赖解析后选定的精确版本。它由 Cargo 管理,不要为了“让项目更干净”手动随意改内容。即使当前练习没有外部依赖,构建后也可能生成这个文件。
target 存放构建产物和中间文件。这里可能迅速变大,但它不是源代码。通常不会把整个 target 提交到版本控制,因为其他人可以根据源码和清单重新构建。
打开 src/main.rs,把内容改成:
fn main() {
let project = "rust_station";
println!("项目 {project} 已经交给 Cargo 管理。");
}注意文件位置是 src/main.rs,不是项目根目录的 main.rs。新手经常在错误位置新建一个同名文件,然后困惑 Cargo 为什么完全不理会修改。Cargo 按项目约定找入口,你改了它不认识的文件,它自然不会替你脑补。
执行 Cargo 命令之前,先看当前目录里是否有 Cargo.toml:
lsPowerShell 可以使用:
Get-ChildItem如果能看到 Cargo.toml,你通常就在项目根目录。Cargo 会从当前目录开始向上寻找清单,但养成在根目录执行命令的习惯更容易判断路径和产物位置。
终端提示找不到 Cargo.toml 时,先检查当前目录,不要先怀疑代码。你可能只是站在了 src 的下一级、项目的上一级,或者另一个同名文件夹里。
现在项目结构已经就位,我们来跑最常用的四条命令。它们不是同一件事的四种拼写,而是对应开发过程中的不同意图。

在项目根目录执行:
cargo checkCargo 会检查当前包及相关依赖能否通过编译,但不会完成最终可执行文件的生成。对一个这么小的项目,你可能感受不到明显速度差;项目变大后,跳过最终代码生成和链接通常能节省时间。
这条命令最适合“我刚改完一小段,想快速知道类型和借用规则有没有问题”的场景。你可以把它理解成向编译器做一次快速对账。编译器仍然会严格指出错误,但它暂时不替你打包成能运行的程序。
故意把代码里的变量名改错:
fn main() {
let project = "rust_station";
println!("项目 {project_name} 已经交给 Cargo 管理。");
}再执行 cargo check,编译器会指出当前作用域中找不到 project_name。它通常还会利用上下文给出相近名称的建议。这里不要只盯着“失败”两个字,箭头位置和建议才是它想和你沟通的重点。
修回 {project} 后,再执行一次:
cargo check看到 Finished 一类的成功信息,就说明静态检查通过。
要得到可执行文件,使用:
cargo buildCargo 默认进行开发构建,也常被口头称为 debug 构建。可执行文件会放在 target/debug 下。在 Linux 或 macOS 上可以直接运行:
./target/debug/rust_stationWindows PowerShell 使用:
.\target\debug\rust_station.exe输出是:
项目 rust_station 已经交给 Cargo 管理。第一次构建时,Cargo 可能需要创建较多中间文件。源代码没有变化时再次构建,它会复用仍然有效的结果,不会把全部内容无条件重做。这个缓存能显著缩短日常迭代时间,也解释了为什么 target 目录会越来越有存在感。
开发小程序时,你通常更想“一条命令编译并运行”:
cargo runCargo 会先判断是否需要重新构建,然后启动生成的程序。源码没变化时,它可以直接复用现有产物;源码有变化时,它会先编译再运行。
cargo run 很方便,但别因此忘记它做了两件事。如果编译失败,程序根本没有开始运行;如果编译成功后程序自己报错,那才进入运行阶段。排错时先分清失败发生在哪一段,能省掉很多乱猜。
例如下面这段代码能够编译,但运行时会主动触发 panic:
fn main() {
panic!("这次是程序运行后主动停止");
}执行 cargo check 会成功,因为语法和类型都合法;执行 cargo run 才会看到 panic 信息。这正好说明“能编译”和“运行时一定顺利”不是同一个承诺。Rust 会在编译阶段挡住许多问题,但不会声称能预知程序所有运行路径和业务条件。
测试完以后,把 src/main.rs 改回正常版本:
fn main() {
let project = "rust_station";
println!("项目 {project} 已经交给 Cargo 管理。");
}初次运行 cargo run 时,终端里通常不只有程序自己的那一行文字。你可能会依次看到 Compiling、Finished 和 Running。这三段正好对应 Cargo 的工作顺序。
Compiling 表示某个包正在编译。项目以后加入依赖时,这里会出现多个包名;它们不是突然混进来的陌生程序,而是依赖图中需要参与本次构建的 crate。第一次构建往往比之后慢,也正是因为 Cargo 需要从较少的缓存开始工作。
Finished dev 表示开发配置的构建已经完成。后面的 unoptimized 和 debuginfo 说明当前产物偏向快速编译和方便调试,没有使用发布配置那样的优化策略。耗时数字会受电脑、项目大小和缓存状态影响,不需要跟教程里的时间对齐。
Running 后面会给出即将启动的可执行文件路径。排查“为什么运行了旧程序”时,这一行很有价值:它直接告诉你 Cargo 启动的是哪个文件。再往下才是程序自己打印的内容。
如果第二次运行时没有出现 Compiling,不要怀疑 Cargo 漏掉了你的项目。源码和相关配置没变化时,它会复用已经构建的结果。反过来,如果你明明修改了 src/main.rs,Cargo 仍然完全没有重新编译,就该检查文件是否保存、终端是否位于另一个同名项目,以及编辑器打开的到底是哪份文件。
有时警告会夹在编译输出中。warning 和 error 的处理方式不同:error 会让当前构建失败;warning 通常允许继续生成程序,但提醒你有未使用变量、可疑写法或未来可能出问题的地方。练习时不要养成无视所有 warning 的习惯,不过也不必把每条警告当成工具链崩溃。先读它指向的代码和建议,再判断该删除、改名还是暂时保留。
Cargo 和 rustup 都自带帮助,不必靠模糊记忆猜参数。查看 Cargo 的总体帮助:
cargo --help查看某个子命令的用法:
cargo new --help
cargo build --helprustup 也可以列出帮助:
rustup --help
rustup toolchain --help帮助输出一般会分成用法、选项和子命令。你不需要从头背到尾,先搜索自己正在考虑的参数,例如 release、edition 或 vcs。这比在命令后不断试不存在的选项更可靠,也能避免把别的语言工具的参数习惯套到 Cargo 上。
当命令执行失败时,还要区分“参数不认识”和“项目编译失败”。前者通常在 Cargo 还没读你的 Rust 源码前就结束,并显示用法提示;后者会出现具体源文件、行号和编译诊断。两个错误都发生在终端里,但负责给出答案的角色不同。
有一个很细却经常发生的误会:cargo new rust_station 会创建项目目录,但不会替你的终端自动 cd 进去。下面两条要分别执行:
cargo new rust_station
cd rust_station如果你创建完项目后直接在原目录执行 cargo run,Cargo 可能会提示找不到清单,或者意外操作外层另一个项目。终端不会因为你脑子里已经“进入项目”就真的改变当前工作目录,它在这件事上相当坚持事实。
刚写下一段代码时,先执行:
cargo check需要观察程序行为时,执行:
cargo run需要明确生成开发构建产物,或者准备让别的工具启动它时,执行:
cargo build你不需要在每次 cargo run 前先手动执行 cargo build。run 会在需要时构建。重复执行不是错误,只是多敲了一条没有必要的命令。
如果你还会在 check、build、run 和 --release 之间犹豫,可以在下面选择“我现在想得到什么”。面板会把命令、是否运行程序和产物目录放在一起对照。
当你执行普通的 cargo build 或 cargo run,Cargo 默认使用开发配置,产物位于 target/debug。它优先考虑编译速度和调试体验,通常包含较丰富的调试信息,优化程度较低。
准备发布,或者要认真测量运行性能时,使用:
cargo build --release这次产物会进入 target/release。release 配置会开启优化,因此程序通常运行得更快,但编译过程会更久,调试时变量和执行顺序也可能因为优化变得不那么直观。
它们不是“错误版本”和“正确版本”的区别,而是两种目标不同的构建方案:

直接运行 release 产物,Linux 或 macOS 使用:
./target/release/rust_stationWindows PowerShell 使用:
.\target\release\rust_station.exe也可以直接构建并运行 release 配置:
cargo run --release我们现在的程序只打印一行文本,主要时间可能花在启动进程和终端输出上。debug 与 release 的差别几乎没有观察价值。不要用这个例子得出“优化没用”或“Rust 两种构建一样快”的结论。
性能测量至少要让被测代码有足够工作量,还要避免把编译时间混进运行时间。认真比较时,应先完成 cargo build --release,然后多次运行 target/release 中的程序。用 cargo run 直接掐一次秒表,会把 Cargo 启动、依赖判断甚至重新编译都混在观察里。
Cargo 把 debug 和 release 放在不同目录,因此你可以保留两套构建结果。这里也埋着一个小坑:刚完成 release 构建后,如果你仍然运行 target/debug/rust_station,启动的还是旧的 debug 版本。
当输出和你预期不一致时,先看命令路径。很多“Cargo 缓存坏了”的故事,最后只是运行了另一个目录里的可执行文件。
性能问题要用 release 产物判断,编译错误则通常先用 cargo check 处理。选择命令的关键不是哪条显得更完整,而是你此刻究竟想得到快速反馈、可执行文件,还是优化后的最终产物。
Rust 的错误信息通常愿意多说几句,这是优点,也可能让刚开始的人觉得信息量过大。排错时不要从“整个 Rust 坏了”开始想,先按失败所处的阶段缩小范围。
典型提示包括 command not found、not recognized as the name of a cmdlet,或者系统表示不是内部或外部命令。
检查顺序如下:
rustup --version,确认 rustup 自身是否可见。PATH 是否包含 Cargo 的 bin 目录。rustc 和 cargo 实际解析到哪个路径。rustup toolchain list 确认可用工具链已经安装。如果 rustup 也找不到,优先处理安装和 PATH。如果 rustup 能用而 rustc 不能用,再处理默认工具链。把这两类情况分开,你就不会在环境变量和工具链安装之间来回碰运气。
这说明 Rust 编译器已经开始工作,问题更靠后。错误里可能出现 linker not found、link.exe not found、cc not found 等信息。
macOS 检查命令行开发工具;Linux 检查本地 C 编译工具和链接器;Windows 检查 MSVC C++ 构建工具及 Windows SDK。安装好前置项后重新打开终端再试。
不要因为错误信息里出现 cc 或 link.exe 就把源文件改来改去。编译器已经告诉你,眼前缺的是生成可执行文件所需的系统工具,而不是 println! 的写法。
一个二进制程序需要入口。例如下面的文件只有普通函数:
fn greet() {
println!("你好");
}直接把它当二进制编译时,编译器会寻找 main,却找不到。补上入口并调用普通函数:
fn greet() {
println!("你好");
}
fn main() {
greet();
}这不是说 Rust 文件里每个函数都必须叫 main,而是一个可执行程序需要且只会从约定入口开始。库项目使用的是另一套组织方式,以后再展开。
在类 Unix 系统上,如果你手动复制或下载了某个可执行文件,可能遇到权限问题。由本机 rustc 正常生成的可执行文件通常已经带有执行权限。如果突然没有权限,先确认你运行的是刚生成的文件,路径没有指向源文件或其他下载内容。
还要确认你输入的是:
./main而不是:
main.rs.rs 是源代码,不是编译后的程序。给源代码乱加执行权限并不能替代编译。
先打印当前目录,再列出文件:
pwd
lsPowerShell 可以使用:
Get-Location
Get-ChildItem你应该在包含 Cargo.toml 的项目根目录执行 Cargo 命令。如果你当前位于 src,回到上一级:
cd ..如果你位于项目外部,就进入正确目录。路径问题看起来不够“技术”,却是入门阶段最高频的故障之一。
先确认你修改的是 Cargo 实际使用的 src/main.rs,不是另一个同名文件。再确认运行的是哪个产物。
使用:
cargo run通常能避免手动选错可执行文件。若你直接运行路径,检查是 target/debug 还是 target/release。直接使用 rustc 时,则要记得修改源文件后重新执行编译命令。
建议按这个顺序:
error,后续错误有时只是它引发的连锁反应。help 建议。cargo check。rustc --explain 查看更完整的说明。例如错误编号为 E0425 时,可以执行:
rustc --explain E0425不要一次凭感觉改五个地方。编译器的诊断是基于当前代码给出的;一次改太多,新的错误和旧的错误纠缠在一起,反而不容易判断哪一步有效。
现代 Rust 工具链能够处理许多包含中文或空格的路径,不能简单说它们一定会失败。但项目还可能调用外部构建脚本、系统编译器和年代不一的第三方工具,这些环节未必同样稳妥。
学习初期使用短一些的英文目录名,是降低无关变量的实用选择,不是 Rust 语法要求。等你熟悉工具链后,再判断具体环境是否能稳定处理复杂路径。这样做的权衡很朴素:少一点目录命名自由,换来更容易复制命令和排查问题。
现在不要只看文字,我们用一个全新的项目把流程串起来。假设你要写一个终端程序,打印本次练习的三个阶段。
先创建并进入项目:
cargo new launch_check
cd launch_check把 src/main.rs 改成:
fn main() {
let stages = ["环境可用", "检查通过", "程序已运行"];
for (index, stage) in stages.iter().enumerate() {
println!("{}. {stage}", index + 1);
}
}这里出现了数组和循环,不要求你现在掌握全部语法。先看直觉:stages 保存三段文字,for 逐个取出它们,enumerate 同时提供从 0 开始的位置,所以打印时加 1。
先检查:
cargo check再运行:
cargo run预期输出:
1. 环境可用
2. 检查通过
3. 程序已运行然后生成两种构建:
cargo build
cargo build --release现在项目里应该同时有 target/debug 和 target/release 两套产物。你不必保留它们来证明自己完成了练习;源代码和 Cargo.toml 才是项目的核心,构建产物可以重新生成。
把循环中的 stages 临时改成不存在的 steps:
for (index, stage) in steps.iter().enumerate() {执行:
cargo check先不要立刻修复。花十秒看清错误指向哪个文件、哪一行,说明当前作用域缺少什么名称,有没有相近建议。然后把它改回 stages,再次执行 cargo check。
这个动作看似多余,却是在练真正有用的能力:你不是只会复制一份永远正确的示例,而是在学习如何和编译器一起把错误缩小。以后所有权和生命周期的诊断会更长,但阅读顺序仍然类似。
关闭当前终端,重新打开一个窗口,在不回看前文的情况下完成下面这组动作:
rustc 与 Cargo 版本。src/main.rs,让它打印一句你能辨认的新文字。如果中间卡住,先判断失败属于命令查找、工具链选择、编译、链接还是运行阶段,再回到对应部分排查。你不需要靠重装解决每个问题,也不用把编译器当成专门挑刺的对手。它更像一个严格但负责的搭档:话有时很多,要求也确实高,但它拦下你的地方,通常都给出了可追踪的理由。
到这里,你已经有了一条可靠的 Rust 开发闭环:用 rustup 维护工具链,在 Cargo 项目里编辑 src/main.rs,用 cargo check 高频对账,用 cargo run 观察行为,需要可执行文件时运行 cargo build,发布和性能测量时切换到 release。下一步遇到更复杂的类型和所有权规则时,我们仍然沿用同一套节奏——写一小段,让编译器检查,读懂它为什么拦你,再继续往前。