学习Rust有很多内容,但每段旅程都有一个起点。在本章中,我们将讨论:
- 在Linux、macOS和Windows上安装Rust
- 编写一个打印“Hello, world!”的程序
- 使用cargo,Rust的包管理器和构建系统
安装
第一步是安装Rust。我们将通过rustup下载Rust,rustup是一个命令行工具,用于管理Rust版本和相关工具。你需要一个网络连接来完成下载。
注意
如果你不想使用rustup进行安装,请参考其他Rust安装方法页面以了解更多选择。
以下步骤将安装Rust编译器的最新稳定版本。Rust的稳定性保证确保了本书中的所有示例代码在新版本的Rust中仍然能够编译成功。输出可能会略有不同,因为Rust经常改进错误消息和警告。换句话说,通过这些步骤安装的任何较新的稳定版Rust应该能够按预期与本书中的内容兼容。
命令行符号
在本章及全书中,我们会展示一些在终端中使用的命令。你应该在终端中输入的行都以字符,它仅仅表示命令行提示符,标识每个命令的开始。以符号的行通常表示前一个命令的输出结果。此外,PowerShell特定的示例会使用>而非$符号。
在Linux或macOS上安装rustup
如果你使用的是Linux或macOS,打开终端并输入以下命令:
$ curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh
该命令会下载一个脚本并开始安装rustup工具,rustup会安装Rust的最新稳定版本。你可能会被提示输入密码。如果安装成功,终端将显示以下信息:
Rust is installed now. Great!
你还需要一个链接器,它是Rust用来将编译后的输出文件合并为一个文件的程序。通常,你已经安装了一个链接器。如果遇到链接器错误,你应该安装一个C编译器,通常C编译器会包含一个链接器。C编译器也是非常有用的,因为一些常见的Rust包依赖于C代码,并且需要C编译器。
在macOS上,你可以通过运行以下命令来获取C编译器:
$ xcode-select --install
Linux用户应该根据其发行版的文档安装GCC或Clang。例如,如果你使用Ubuntu,你可以安装build-essential包。
在Windows上安装rustup
在Windows上,访问 Rust官方安装页面 并按照提示安装Rust。在安装过程中,你会看到一条消息,解释你还需要安装Visual Studio 2013或更高版本的MSVC构建工具。
要获取构建工具,你需要从 Visual Studio官网 安装Visual Studio 2022。在安装过程中,选择以下工作负载:
- “Desktop Development with C++”
- Windows 10或11 SDK
- 英语语言包组件(以及你选择的其他语言包)
本书中的命令适用于cmd.exe和PowerShell。如果有特定差异,我们会说明使用哪一个。
故障排除
要检查Rust是否正确安装,打开终端并输入以下命令:
$ rustc --version
你应该看到类似以下格式的版本号、提交哈希和提交日期:
rustc x.y.z (abcabcabc yyyy-mm-dd)
如果看到这些信息,说明Rust已成功安装!如果没有看到这些信息,请检查Rust是否已添加到你的%PATH%系统变量中,方法如下:
- 在Windows CMD中,使用:
> echo %PATH%
- 在PowerShell中,使用:
> echo $env:Path
- 在Linux和macOS中,使用:
$ echo $PATH
如果这些都正确但Rust仍无法工作,你可以通过 Rust社区页面 寻求帮助,了解如何与其他Rustaceans(我们自己取的一个有趣的绰号)联系。
更新和卸载
一旦通过rustup安装了Rust,更新到新发布的版本非常简单。在终端中运行以下更新脚本:
$ rustup update
要卸载Rust和rustup,可以在终端中运行以下卸载脚本:
$ rustup self uninstall
本地文档
Rust的安装还包括一个本地副本的文档,方便你离线阅读。运行rustup doc命令可以在浏览器中打开本地文档。
每当你遇到标准库中提供的类型或函数,且不确定它的功能或用法时,可以查看应用程序编程接口(API)文档来了解它!
Hello, World!
现在你已经安装了Rust,是时候编写你的第一个Rust程序了。在学习一门新语言时,通常会写一个打印“Hello, world!”到屏幕的小程序,我们也将遵循这个传统!
注意
本书假设你对命令行有基本的了解。Rust对编辑工具或代码存放位置没有特定要求,所以如果你更喜欢使用集成开发环境(IDE)而非命令行,完全可以使用你喜欢的IDE。现在许多IDE都支持Rust;你可以查阅IDE的文档了解详细信息。Rust团队也在通过rust-analyzer实现良好的IDE支持,详情请参见附录D。
创建项目目录
首先,你需要创建一个目录来存储你的Rust代码。Rust并不关心你的代码存放在哪里,但为了方便本书中的练习和项目,建议你在主目录下创建一个projects目录,并将所有项目存放在该目录下。
打开终端,输入以下命令来创建projects目录,并在其中为“Hello, world!”项目创建一个子目录:
-
对于Linux、macOS和Windows PowerShell,输入以下命令:
$ mkdir ~/projects $ cd ~/projects $ mkdir hello_world $ cd hello_world -
对于Windows CMD,输入以下命令:
> mkdir "%USERPROFILE%\projects" > cd /d "%USERPROFILE%\projects" > mkdir hello_world > cd hello_world
编写和运行Rust程序
接下来,创建一个新的源文件,命名为main.rs。Rust文件的扩展名总是以.rs结尾。如果文件名包含多个单词,约定使用下划线分隔它们。例如,应该使用hello_world.rs而不是helloworld.rs。
现在,打开你刚刚创建的main.rs文件,并输入以下代码:
fn main() {
println!("Hello, world!");
}
Listing 1-1: 打印“Hello, world!”的程序
保存文件并回到终端窗口(仍在~/projects/hello_world目录下)。在Linux或macOS上,输入以下命令来编译并运行该文件:
$ rustc main.rs
$ ./main
Hello, world!
在Windows上,使用命令.\main.exe来代替./main:
> rustc main.rs
> .\main.exe
Hello, world!
无论你使用哪个操作系统,终端中应该会打印出Hello, world!。如果没有看到这个输出,请参考第3页的“故障排除”部分,了解如何获得帮助。
如果你看到了“Hello, world!”的输出,恭喜你!你已经正式编写了第一个Rust程序。这意味着你已经成为一名Rust程序员——欢迎加入!
Rust程序的结构
让我们详细回顾一下这个“Hello, world!”程序。首先是程序的第一部分:
fn main() {
}
这几行定义了一个名为main的函数。main函数是特殊的:它总是每个可执行Rust程序中首先运行的代码。在这里,第一行声明了一个名为main的函数,这个函数没有参数,也没有返回值。如果函数有参数,它们会写在圆括号()内。
函数的主体被花括号{}包裹。Rust要求所有的函数体都使用花括号。这是一个良好的编码风格,将花括号的开头放在函数声明的同一行,并在花括号与函数名之间留一个空格。
注意
如果你想在Rust项目中坚持使用统一的风格,可以使用一个自动格式化工具叫做rustfmt,它会按照特定的风格格式化你的代码(有关rustfmt的更多内容,请参见附录D)。Rust团队已将这个工具与标准Rust发行版一起提供,就像rustc一样,所以它应该已经安装在你的计算机上了!
main函数的主体包含以下代码:
println!("Hello, world!");
这一行做了程序中的所有工作:它将文本打印到屏幕上。这里有四个重要的细节需要注意:
- 缩进
Rust的编码风格要求使用四个空格进行缩进,而不是Tab键。 - println!是一个宏
println!调用了Rust的一个宏。如果它调用的是函数,而不是宏,它的写法应该是println(没有感叹号)。我们将在第19章详细讨论Rust宏。现在你只需要知道,使用感叹号!意味着你调用的是一个宏,而不是普通的函数,并且宏的规则有时与函数不同。 - 字符串参数
你可以看到字符串"Hello, world!"。我们将这个字符串作为参数传递给println!宏,结果就是字符串被打印到屏幕上。 - 分号结尾
我们在代码行末尾加上分号(;),这表示该表达式已结束,下一条表达式可以开始。大多数Rust代码行都以分号结尾。
这样,我们就逐步分析了这个简单的Rust程序的结构。
编译和运行是两个独立的步骤
你刚刚运行了一个新创建的程序,让我们来逐步分析这个过程中的每个步骤。
在运行一个Rust程序之前,你必须使用Rust编译器编译它。通过输入rustc命令并将源文件的名称传递给它,你可以编译程序,如下所示:
$ rustc main.rs
如果你有C或C++的背景,你会注意到这与gcc或clang非常相似。编译成功后,Rust会输出一个二进制可执行文件。
查看生成的可执行文件
在Linux、macOS和Windows的PowerShell中,你可以通过在终端中输入ls命令来查看可执行文件:
$ ls
main main.rs
在Linux和macOS中,你会看到两个文件。而在Windows的PowerShell中,你会看到与CMD中相同的三个文件。在Windows的CMD中,你可以输入以下命令:
> dir /B %= /B选项表示只显示文件名 =%
main.exe
main.pdb
main.rs
这会显示源代码文件(带有.rs扩展名)、可执行文件(在Windows上是main.exe,在其他平台上是main)以及在Windows上调试信息的.pdb扩展名的文件。接下来,你可以运行main或main.exe文件,如下所示:
$ ./main # 在Windows上是 .\main.exe
如果你的main.rs是“Hello, world!”程序,这一行会将Hello, world!打印到你的终端上。
动态语言与Rust的区别
如果你更熟悉像Ruby、Python或JavaScript这样的动态语言,你可能不习惯将编译和运行程序视为两个独立的步骤。Rust是一种提前编译的语言,这意味着你可以编译程序并将可执行文件交给别人运行,即使他们没有安装Rust。如果你将一个.rb、.py或.js文件交给别人,他们需要分别安装Ruby、Python或JavaScript的运行环境。但在这些语言中,你只需要一个命令就能编译并运行程序。这种设计方式在语言设计中是一个权衡。
使用rustc进行编译
对于简单的程序,仅使用rustc进行编译就足够了。但随着项目的增长,你可能希望管理更多的选项,并使代码共享变得更加容易。接下来,我们将介绍Cargo工具,它将帮助你编写更为复杂和真实世界中的Rust程序。
你好,Cargo!
Cargo是Rust的构建系统和包管理器。大多数Rustaceans(Rust开发者)使用这个工具来管理他们的Rust项目,因为Cargo为你处理了很多任务,比如构建代码、下载代码依赖的库以及构建这些库。(我们称这些库为“依赖”)
像我们到目前为止写的最简单的Rust程序,并没有任何依赖。如果我们用Cargo来构建“Hello, world!”项目,它只会使用Cargo中处理代码构建的部分。当你编写更复杂的Rust程序时,你会添加依赖,而如果你用Cargo开始一个项目,添加依赖会变得更加容易。
因为绝大多数Rust项目都使用Cargo,接下来的内容将假设你也在使用Cargo。如果你使用的是官方安装程序(在第1页的“安装”部分讨论过),Cargo会随着Rust一起安装。如果你通过其他方式安装了Rust,可以通过在终端中输入以下命令来检查Cargo是否已经安装:
$ cargo --version
如果你看到版本号,说明已经安装!如果看到类似“命令未找到”的错误,请参考你的安装方法的文档,查看如何单独安装Cargo。
使用Cargo创建一个项目
让我们使用Cargo创建一个新项目,并看看它与我们最初的“Hello, world!”项目有什么不同。返回到你的项目目录(或者你决定存储代码的任何目录),然后在任何操作系统上运行以下命令:
$ cargo new hello_cargo
$ cd hello_cargo
第一个命令创建了一个新的目录,并在其中生成了一个名为hello_cargo的项目。我们将项目命名为hello_cargo,Cargo会在一个同名的目录中创建相关文件。
进入hello_cargo目录并列出文件。你会看到Cargo为我们生成了两个文件和一个目录:一个Cargo.toml文件和一个包含main.rs文件的src目录。
它还初始化了一个新的Git仓库,并生成了一个.gitignore文件。如果你在已有Git仓库中运行cargo new,则不会生成Git文件;你可以通过使用cargo new --vcs=git来覆盖此行为。
注意
Git是一个常用的版本控制系统。你可以通过使用--vcs标志来改变cargo new使用的版本控制系统,或者不使用版本控制系统。运行cargo new --help查看可用选项。
打开你喜欢的文本编辑器,查看Cargo.toml文件。它应该类似于Listing 1-2中的代码:
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
Listing 1-2: Cargo.toml文件的内容
此文件采用TOML(Tom’s Obvious, Minimal Language)格式,这是Cargo的配置格式。
第一行[package]是一个节标题,表示接下来的语句是在配置一个包。随着我们向这个文件添加更多内容,其他配置节也会被添加。
接下来的三行设置了Cargo编译程序所需的配置信息:包名、版本和使用的Rust版本。我们将在附录E中讨论edition键。
最后一行[dependencies]表示你可以在此列出项目的依赖项。在Rust中,代码包被称为crates。对于这个项目,我们不需要任何其他crates,但在第二章的第一个项目中,我们将需要依赖项,那时我们会使用这个依赖节。
现在打开src/main.rs并查看内容:
fn main() {
println!("Hello, world!");
}
Cargo为你生成了一个“Hello, world!”程序,和我们在Listing 1-1中写的完全一样!到目前为止,我们的项目和Cargo生成的项目之间的不同之处在于,Cargo将代码放在了src目录中,并且在顶层目录中有一个Cargo.toml配置文件。
Cargo期望你的源文件放在src目录中。顶层的项目目录仅用于README文件、许可证信息、配置文件以及其他与代码无关的内容。使用Cargo可以帮助你组织项目:每件事物都有它的位置,一切都井井有条。
如果你启动了一个不使用Cargo的项目(就像我们创建的“Hello, world!”项目),你可以将它转换为一个使用Cargo的项目。将项目代码移到src目录,并创建一个适当的Cargo.toml文件。
构建和运行Cargo项目
现在让我们看看使用Cargo构建和运行“Hello, world!”程序时有什么不同!在hello_cargo目录下,使用以下命令构建项目:
$ cargo build
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs
这个命令会在target/debug/hello_cargo目录(或者在Windows上是target\debug\hello_cargo.exe)生成一个可执行文件,而不是在当前目录下生成。因为默认构建是调试版本,所以Cargo将二进制文件放在一个名为debug的目录中。你可以使用以下命令运行该可执行文件:
$ ./target/debug/hello_cargo # 或者在Windows上使用 .\target\debug\hello_cargo.exe
Hello, world!
如果一切顺利,Hello, world!应该会打印到终端。第一次运行cargo build时,Cargo还会在顶层目录创建一个新文件:Cargo.lock。该文件跟踪项目中依赖项的确切版本。由于这个项目没有依赖项,文件内容会比较简单。你无需手动修改这个文件,Cargo会自动管理它的内容。
我们刚才使用cargo build构建了项目并通过./target/debug/hello_cargo运行了它,但我们也可以使用cargo run来一次性编译代码并运行生成的可执行文件:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/hello_cargo`
Hello, world!
使用cargo run比先运行cargo build再手动指定二进制文件路径要方便得多,因此大多数开发者都会使用cargo run。注意,这次我们没有看到Cargo编译hello_cargo的输出,因为Cargo发现文件没有发生变化,所以没有重新编译,而是直接运行了二进制文件。如果你修改了源代码,Cargo会在运行之前重新编译项目,你会看到类似以下的输出:
$ cargo run
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
Running `target/debug/hello_cargo`
Hello, world!
Cargo还提供了一个名为cargo check的命令。这个命令快速检查你的代码,确保它可以编译,但不会生成可执行文件:
$ cargo check
Checking hello_cargo v0.1.0 (file:///projects/hello_cargo)
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
为什么不想要生成可执行文件呢?通常,cargo check比cargo build快,因为它跳过了生成可执行文件的步骤。如果你在编写代码时不断检查工作,使用cargo check会加速你检查项目是否能够编译的过程。因此,许多Rustaceans会在编写程序时定期运行cargo check来确保代码能够编译成功,然后在准备好使用可执行文件时再运行cargo build。
到目前为止,我们已经学到了关于Cargo的以下内容:
- 我们可以使用
cargo new创建一个项目。 - 我们可以使用
cargo build构建项目。 - 我们可以使用
cargo run一步构建并运行项目。 - 我们可以使用
cargo check构建项目而不生成二进制文件,以便检查代码错误。 - Cargo将构建结果保存在
target/debug目录中,而不是与代码文件存放在同一目录下。
使用Cargo的另一个优点是,无论你在哪个操作系统上工作,命令都是一样的。因此,从现在开始,我们将不再为Linux、macOS和Windows提供不同的指令。
为发布构建
当你的项目终于准备好发布时,可以使用cargo build --release来进行带有优化的编译。这个命令会在target/release目录中生成一个可执行文件,而不是在target/debug中。启用优化可以让你的Rust代码运行得更快,但它也会增加程序的编译时间。这就是为什么有两个不同的构建配置:一个用于开发,当你需要快速并频繁地重新构建时,另一个用于构建最终发布的程序,这个程序不会被反复重建,并且会尽可能地运行得更快。如果你在对代码的运行时间进行基准测试,记得使用cargo build --release构建并使用target/release中的可执行文件进行基准测试。
Cargo作为约定
对于简单的项目,Cargo的优势可能不如直接使用rustc那样明显,但当你的程序变得更复杂时,Cargo的价值就会体现出来。一旦程序增长到包含多个文件或需要依赖项时,让Cargo协调构建会更加方便。
即使hello_cargo项目本身很简单,它现在已经使用了你在接下来的Rust开发过程中会用到的大部分工具。事实上,要处理任何现有的项目,你可以使用以下命令通过Git克隆代码,进入项目目录并构建:
$ git clone example.org/someproject
$ cd someproject
$ cargo build
有关Cargo的更多信息,请查看其文档:Cargo文档。
总结
你已经在Rust学习旅程上迈出了坚实的步伐!在本章中,你学会了如何:
- 使用
rustup安装最新的稳定版Rust - 升级到更新的Rust版本
- 打开本地安装的文档
- 使用
rustc直接编写并运行“Hello, world!”程序 - 使用Cargo的约定创建并运行一个新项目
现在是构建一个更复杂的程序,熟悉Rust代码的好时机。因此,在第2章中,我们将构建一个猜数字游戏程序。如果你更愿意先学习Rust中常见的编程概念,可以查看第3章,然后再回到第2章继续学习。