第一章:环境搭建

目录

第一章:环境搭建

啊,第一步。在空白画布上全新启程的感觉不是很美妙吗?然后选定那块你将用来建造整个宫殿的基石?

遗憾的是,当你构建计算机程序时,第一步可能会变得......复杂且令人沮丧。你必须确保开发环境已针对所使用的编程语言进行配置,还要弄清楚如何在该环境中编译和运行程序。

幸运的是,Rust 自带的开发环境已为我们完成了大部分工作,因此除了文本编辑器外你几乎不需要其他配置。本教程虽然是在 Mac 系统上编写的,但同样适用于 Windows 和 Linux 系统。

我们将使用 rustup 来安装 Rust,该工具可以管理已安装的 Rust 版本及相关工具链。

Rust 附带了一本极好的免费书籍:《Rust 编程语言》。每当书中解释得比我更透彻时,我会经常链接到这本书以获取更深入的知识。要安装 rustup ,只需按照第一章第一节的安装指南操作即可。在撰写本文时,Mac 上的安装流程如下:打开终端(通过 Spotlight 搜索"终端"找到),然后输入以下命令:

arduino 复制代码
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

安装程序会显示若干选项,按 Enter 即可选择默认配置。安装结束时会出现如下鼓舞人心的提示:

csharp 复制代码
Rust is installed now. Great!

重启终端后,输入以下命令:

css 复制代码
rustc --version

您应该能看到已安装版本的相关信息。如果未显示,请查阅安装指南进行故障排查。

顺便说一句,本教程会频繁使用终端,所以请保持终端开启状态。

The main() function

创建一个名为 hecto.rs 的新文件,并在其中编写一个 main() 函数( hecto 是我们正在构建的文本编辑器的名称)。

rust 复制代码
fn main(){
	return;
}

在 Rust 中,所有可执行代码都必须放在函数内部。Rust 中的 main() 函数具有特殊意义,它是程序运行时默认的起始点。当从 main() 函数返回时,程序将退出并将控制权交还给操作系统。

Rust 是一门编译型语言。这意味着我们需要通过 Rust 编译器将程序转换为可执行文件,然后就可以像在命令行中运行其他程序一样运行这个可执行文件。

要编译 hecto.rs ,请在终端中运行 rustc hecto.rs 。如果没有错误发生,这将生成一个名为 hecto 的可执行文件。要运行 hecto ,输入 ./hecto 并按 Enter 。程序不会输出任何内容。

使用 cargo 进行编译

理论上, rustc 就是构建 hecto 所需的全部。实际上,Rust 附带了一些便利工具,能让开发更轻松,并且如你稍后将看到的,也更安全。

Rust 附带了一个名为 cargo 的程序。如果你熟悉 JavaScript 生态系统,那么 cargo 可以最贴切地描述为 Rust 版的 npm 。它帮助你管理依赖项、编译代码以及其他事项。正确安装 Rust 后,你可以输入以下命令来调用 cargo:

css 复制代码
cargo --version

我们将使用这个程序来开始 hecto 项目。通过输入 rm hecto.rs hecto ,删除你之前的 hecto.rs 和可执行文件 hecto

在您希望 hecto 诞生的目录中,输入以下命令:

css 复制代码
cargo init hecto --vcs none

使用 --vcs none 参数可确保您初始化 hecto 时不启用 git 支持。若要在本项目中使用 git ,请省略此标志(但这样您需要先安装 git

cargo 会通过以下提示告知您初始化成功:

java 复制代码
 Created binary (application) package

它创建了一个名为 hecto 的新文件夹,其中包含若干文件。稍后我们将逐一查看这些文件,最值得关注的是位于 src 文件夹中名为 main.rs 的文件。

让我们来看看这个文件。当你打开它时,会发现里面已经包含了一个 main() 。仅从内容就能推断出,这个程序遵循古老的传统------打印出"Hello, World!"然后退出。

要使用 cargo 编译这个程序,请确保你位于 hecto 文件夹中(在 cargo init hecto 后运行 cd hecto ),然后在终端运行 cargo build 。与 rustc 不同, cargo 不会保持静默,它会输出类似这样的内容:

scss 复制代码
	Compiling hecto v0.1.0 (/Users/pflenker/repositories/hecto)
    Finished dev [unoptimized + debuginfo] target(s) in 12.36s

运行此命令会添加更多文件(我们稍后会查看这些文件),并将一个名为 hecto 的可执行文件放入 target/debug 文件夹中。让我们执行 ./target/debug/hecto 来确认这个程序确实会输出 Hello, World! 然后退出。

理解 cargo 的额外文件(及功能)

现在我们的 hecto 目录下包含了许多内容(其中有些是隐藏文件,具体内容可能因机器而异):

  1. 一个名为 src 的文件夹,内含 main.rs
  2. 两个分别名为 Cargo.tomlCargo.lock 的文件
  3. 一个名为 target 的文件夹,里面有一些隐藏文件,还有一个名为 debug 的文件夹。该文件夹包含更多文件和子文件夹,以及我们的可执行文件 hecto 。这些额外的东西有什么用呢?让我们来了解一下。

src 文件夹

src 文件夹将存放我们所有的源代码文件。目前只有一个文件,很快会添加更多。

Cargo.tomlCargo.lock

这个文件遵循名为 TOML 的配置格式,用于向编译器传递待编译代码的相关信息。我们将在后续段落详细查看默认配置文件的内容。

cargo 还为我们处理依赖管理,而 Cargo.toml 会帮我们保存这些依赖项。 Cargo.lock 是依赖管理的一部分,它能确保不同环境中的依赖项保持一致。现在不必担心这个问题------我们将在下一章深入探讨。

Build Targets

让我们来看看 target 及其内容。

cargo 支持多种所谓的构建目标。默认使用的是 debug ,这意味着最终的可执行文件主要面向我们开发者,而非实际的终端用户。

另一个有效的目标可能是 release ,这正好相反:它是面向客户的产品,而不仅仅是开发者工具。

要构建发布版本,请运行:

arduino 复制代码
cargo build --release

完成后,你会注意到 target 文件夹中出现了另一个恰如其名称为 release 的文件夹。

等等,"可执行文件面向特定人群"是什么意思?它不就只是打印"Hello World"吗,不管谁使用都一样?

确实如此。

首先,Rust 编译器会尽量为开发者和终端用户提供便利,因此它对 debugrelease 构建采取了不同的处理方式。稍后我们会遇到一个具体例子,但存在几类编程错误时,Rust 会认定你绝对、绝对不是有意为之。如果 debug 构建遇到这类错误,程序会直接崩溃------这是 Rust 能采取的最极端手段,以此表明你的错误有多严重。但 Rust 其实具备从这类错误中恢复的能力,可以让程序继续运行(尽管可能产生错误结果)。考虑到对终端用户而言,错误结果固然糟糕,但程序崩溃更难以接受,因此同样的错误在 release 构建中就不会导致崩溃。

其次,编译器可以在后台执行各种优化,使结果运行得更快。但这些优化需要时间,会拖慢编译速度。谁需要频繁编译?开发者。谁从不编译?用户。因此, debug 构建版本会禁用优化以优先考虑开发速度,而 release 构建版本则启用优化以优先考虑执行速度。

最后,这个可执行文件不仅仅包含"Hello, World"代码,Rust 还会在 debug 构建中添加有助于更快调试问题的信息。这些信息对客户来说并不需要,因此可以从发布版本中排除。

cargo 在编译过程中也向我们传达了这一点, debug 版本的最终行如下所示:

scss 复制代码
Finished dev [unoptimized + debuginfo] target(s) in 0.04s

这告诉我们该构建版本未经优化且包含调试信息。

release 构建版本在编译结束时显示:

scss 复制代码
Finished release [optimized] target(s) in 0.04s

这表明该版本不包含调试信息,且是优化后的构建版本。

让我们仔细看看调试信息,但别动我们崭新且已深爱的文本编辑器代码。直接前往 Rust Playground,那里已经贴心地内置了我们的"hello world"函数。在左上角你会看到几个按钮:先是 Debug,接着是 Stable,然后是 ... 。点击这三个点并启用 backtrace

接着按以下方式修改代码沙盒中的内容:

rust 复制代码
fn main() {
   panic!("Hello, World");
}

panic! 会直接引发崩溃,简单明了。点击"运行"执行程序,然后切换至"调试"模式,将其改为"发布"模式后再次运行。

你会注意到 release 构建生成了以下输出:

arduino 复制代码
     Running `target/release/playground`
thread 'main' panicked at src/main.rs:2:4:
Hello, World
stack backtrace:
   0: rust_begin_unwind
   1: core::panicking::panic_fmt
   2: playground::main

debug 的输出看起来像这样:

bash 复制代码
    Running `target/debug/playground`
thread 'main' panicked at src/main.rs:2:4:
Hello, World
stack backtrace:
   0: rust_begin_unwind
             at /rustc/7cf61ebde7b22796c69757901dd346d0fe70bd97/library/std/src/panicking.rs:647:5
   1: core::panicking::panic_fmt
             at /rustc/7cf61ebde7b22796c69757901dd346d0fe70bd97/library/core/src/panicking.rs:72:14
   2: playground::main
             at ./src/main.rs:2:4
   3: core::ops::function::FnOnce::call_once
             at /rustc/7cf61ebde7b22796c69757901dd346d0fe70bd97/library/core/src/ops/function.rs:250:5

我们尚未详细讨论堆栈跟踪,但即使不了解相关知识,你也能立即看出调试输出比发布版本包含更多信息。理解调用栈(即函数调用其他函数的顺序,直至某个函数崩溃)会非常有用。

构建产物

这样我们就解释了文件夹的用途,但那些不是 hecto 的内容又是什么呢?让我们深入研究一下来增进理解。

回到终端,依次运行以下命令:

复制代码
cargo clean
cargo build
cargo build

(是的,我们运行了两次 cargo build

第一条命令会清理 target 目录。第二条命令和之前一样执行。但在第三条命令执行时, cargo 不再像之前那样输出以 Compiling 开头的行。这是因为 cargo 能识别出当前版本的 main.rs 已经完成编译。如果自上次编译后 main.rs 未被修改, cargo 就不会重复执行编译过程。若 main.rs 发生变动, cargo 则会重新编译 main.rs 。随着代码库规模扩大,这个机制会愈发实用------当你仅修改某个组件的源代码时,大多数组件无需被反复重新编译。

target 目录中所有(好吧:大部分)额外文件都是构建产物,它们能让 cargo 在后续编译时更快完成。更多详情请参阅 cargo's 文档。

编译与运行

由于编译和运行程序是非常常见的需求, cargo 通过 cargo run 命令将这两个步骤合二为一。

尝试将 main.rs 中的返回值更改为 Hello, World 以外的字符串。然后运行 cargo run ,你应该能看到它编译。检查结果是否显示你修改后的字符串。接着将其改回 Hello, World ,重新编译,确保程序恢复为返回 Hello, World 的状态。

代码审查

让我们一起来看这段代码。

Open this step on GitHub

点击上方链接,您将看到一个带注释的提交记录。它会展示从上一步到当前步骤之间的所有代码变更,并在相关代码行旁直接显示注释说明。

在这种情况下,我描述的是该提交中 Cargo.toml 的内容。

采用这种提交方式工作有很多优点:

  • 你可以直接在相关代码上看到解释,更容易理解代码变更。
  • 你可以直接提问(需要 GitHub 账号)或对不理解的代码片段发表评论

刚开始你可能需要在本教程和 GitHub 之间来回切换,这会有些累人。不过等我们掌握了基础知识后,我将不再提前展示我的操作让你重复------而是先告诉你任务要求,之后再展示我的解决方案,这样你就不需要频繁切换 GitHub 了。

在本教程中,我会提供大量延伸资料的链接。这里说明一下链接的使用规范:理解教程所需的关键信息会直接呈现在 GitHub 提交记录或正文内容中。其他未明确标注为 GitHub 操作步骤的链接均属于可选内容,不感兴趣的话可以跳过。

总结与展望

在本章中,我们安装了 Rust,初始化了一个基础项目并熟悉了相关操作。现在我们已经掌握了手动编译和使用 cargo 编译代码的方法,并对 cargo 及其生成的文件有了初步认识。最重要的是,我们能够构建项目并在需要时清理生成文件。我们还认识了 Rust Playground 工具,当我们需要先独立测试新功能再投入实际使用时,这个工具肯定会派上用场。

在第二章中,我们将构建一个能读取用户输入、在屏幕上显示内容,并在按下 q 时退出的程序。仅这个简单的程序就包含大量值得初学者掌握的知识点。

  1. 终有一天,你将能用 hecto 来编辑 hecto ,甚至不再需要文本编辑器。
  2. Rust 支持异步编程(本教程不涉及此内容)。在这种情况下, main 完全有可能已执行完毕却尚未将控制权交还给操作系统。
  3. 不知你是否有同感,但创建一个真实可用的、成熟的应用程序对我来说始终是充满魔力的时刻。
  4. 根据你的知识背景,可能觉得这个问题很奇怪,毕竟构建变体的概念如今已非常普遍。但过去情况并非如此。
相关推荐
萧曵 丶1 小时前
Rust 中的返回类型
开发语言·后端·rust
浪裡遊3 小时前
Sass详解:功能特性、常用方法与最佳实践
开发语言·前端·javascript·css·vue.js·rust·sass
受之以蒙4 小时前
Rust & WASM 之 wasm-bindgen 基础:让 Rust 与 JavaScript 无缝对话
前端·笔记·rust
Pomelo_刘金1 天前
Rust 宣布发布1.88.0
rust
寻月隐君1 天前
告别 Vec!掌握 Rust bytes 库,解锁零拷贝的真正威力
后端·rust·github
大卫小东(Sheldon)1 天前
GIM 1.5发布了! 支持Windows系统了
git·ai·rust
寻月隐君1 天前
告别竞态条件:基于 Axum 和 Serde 的 Rust 并发状态管理最佳实践
后端·rust·github
成遇11 天前
在Vscode中安装Sass并配置
vscode·rust·sass
止观止11 天前
Rust智能指针演进:从堆分配到零复制的内存管理艺术
开发语言·后端·rust