rust-lang-zh_CN/src/Ch01_Getting_Started.md
2023-04-08 22:33:35 +08:00

22 KiB
Raw Blame History

入门

现在就开始 Rust 之旅!有很多要掌握的东西,不过千里之行,始于足下。本章将讨论:

  • 在 Linux、macOS 及 Windows 上安装 Rust;
  • 编写一个打印出 Hello, world! 的程序来;
  • Rust 的包管理器和构建系统 Cargo 的使用。

安装

第一步即是安装 Rust。这里将通过 rustup 这个用于管理 Rust 版本及相关工具的命令行工具,来下载 Rust。要下载 Rust就需要互联网连接。

注意:若由于某些原因而不愿使用 rustup,那么请参考 其他 Rust 安装方式页面 了解更多选项。

接下来就是要按照最新的稳定版 Rust 编译器。Rust 的稳定性保证了本书中所有示例都将在较新的 Rust 版本下可持续编译。由于 Rust 经常会改进错误消息和告警,因此在不同版本之间,输出可能会略有不同。也就是说,任何使用以下步骤所安装的较新、稳定版 Rust都将如本书内容中所期望的那样工作。

关于命令行注释 在本章及全书中,都会给出一些在终端中用到的命令。他们是一些应在以 $ 开始的终端中输入的行。至于这个 $ 字符,是无需输入的;这个字符表示每条命令的开头。那些不以 $ 开头的行,通常给出的是上一命令的输出。此外,那些特定于 PowerShell 的示例中,将使用 > 而不是 $

在 Linux 与 macOS 上安装 rustup

若使用的是 Linux 或 macOS那么请打开一个终端然后输入下面的命令

$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

此命令会下载一个脚本并开始 rustup 工具的安装,而 rustup 将安装最新的稳定版 Rust。可能会提示输入 sudo 密码。在安装成功后,就会出现下面这行!

Rust is isntalled now. Great!

这里还将需要一个连接器linker这是个Rust要用来将其编译好的输出组合起来形成一个文件的程序。似乎你的电脑上以及有了一个这样的连接器了。若收到连接器错误信息那么就应安装一个 C 语言编译器C 编译器通常会包含着连接器的。由于一些常用 Rust 包对 C 代码有依赖且需要 C 编译器,因此 C 编译器也是有用的。

在 macOS 上,可通过运行下面的命令,获取到一个 C 编译器:

$ xcode-select --install

Linux 用户一般都会安装 GCC 或 Clang至于具体哪种 C 编译器,则是依据他们所用 Linux 分发版本的文档可以确定。比如若使用的是 Ubuntu那么就可以安装 build-essential 软件包。

在 Windows 上安装 rustup

在 Windows 上,请前往 https://www.rust-lang.org/tools/install 页面,并按照安装 Rust 的指令进行安装。在安装过程的某个时刻,将收到为何需要 Visual Studio 2013 或更新版本的 C++ 构建工具的说明。而最简单的获取到构建工具的方法,则是安装 Visual Studio 2019 构建工具。在询问将要安装何种工作负载workloads请确保 C++ build tolls 被选中,还要确保包含 Windows 10 SDK 及英语语言包。

本书接下来用到的命令,在 cmd.exePowerShell 中都可工作。若其中有特定区别,本书将会解释要用哪个。

更新与卸载

在通过 rustup 安装了 Rust 后,更新到最新版本就容易了。在 shell 中运行下面的更新脚本:

$ rustup update

而要卸载 Rust 和 rustup,只需在 shell 中运行下面的卸载脚本:

$ rustup self uninstall

问题排除

要检查当前是否安装了 Rust, 请开启一个 shell 并敲入这行命令:

$ rustc --version

就会看到版本编号、合并哈希(commit hash以及已发布的该最新稳定版本合并日期以下面这种格式

rustc x.y.z (abcabcadc yyyy-mm-dd)

若看到这个信息,那么就已成功安装了 Rust若看不到这个信息且是在 Windows 上,那么就请在 %PATH% 系统变量中检查一下 Rust 在不在里面。若那一点问题都没有而 Rust 仍就不工作,那么可在数个地方需求帮助。其中最便利的就是 Rust 官方 Discord 上的 #beginners 频道了。在那里可与其他 Rust 公民(一种无厘头的自我称呼)聊天,他们可以帮助到你。其他不错的资源包括 用户论坛Stack Overflow

本地文档

Rust 的安装,也包含了一份本地文档,因此可离线阅读到这本地文档。运行 rustup doc 即可在浏览器中打开这本地文档。

在任何时候遇到标准库所提供的类型或函数,而又确定他做些什么或该怎样使用这类型或函数时,就可以使用 API 文档来搞明白他是怎么回事!

Hello, World!

既然已经安装好了 Rust, 那么就来编写第一个 Rust 程序吧。在掌握一门新语言时,传统就是要编写一个小的、打印出文字 Hello, World! 到屏幕上的程序,因此这里也会干这同样的事情!

注意本书假定读者对命令行有着基本的熟悉。Rust 对代码在何处编辑和使用何种工具编辑没有特别要求,因此若优先选择某种集成开发环境,而非命令行,那么使用喜好的 IDE 即可。许多 IDE 都有某种程度的 Rust 支持;请查看 IDE 文档了解有关细节信息。近来Rust 团队已着手启动良好的IDE支持且此方面已取得极大进展

创建一个项目目录

这里是以构造一个保存 Rust 代码的目录开始的。对于 Rust 来说,代码位居何处并不重要,不过对于本书中的练习与项目,是建议在主目录下构造一个 projects 目录,并把全部项目放在那里的。

请打开一个终端,并输入下面的这些命令来构造一个 projects 的目录,和一个在 projects 下用于 "Hello, World!" 项目的目录。

对于 Linux、macOS 和 Windows 上的 PowerShell, 请输入:

$ mkdir ~/rust-lang/projects
$ cd ~/rust-lang/projects
$ mkdir hello_world
$ cd hello_world

而对于 Windows 的 CMD 请输入:

> mkdir "%USERPROFILE%\rust-lang\projects"
> cd /d "%USERPROFILE%\rust-lang\projects"
> mkdir hello_world
> cd hello_world

编写及运行 Rust 程序

接下来,就要构造一个源代码文件,并命名为 main.rs。Rust 文件总是以 .rs 扩展名结束。若要在文件名中是一多个单词,那么请使用下划线来将这些单词隔开。比如,请使用 hello_world.rs 而不是 helloworld.rs

现在就要打开这个刚创建出的 main.rs 文件,并敲入清单 1-1 中的代码。

文件名:main.rs

fn main() {
    println!("Hello, World!");
}

清单 1-1打印Hello, World! 的程序

保存这个文件并回到终端窗口。在 Linux 或 macOS 上,请输入下面的命令来编译和运行这个文件:

$ rustc main.rs
$ ./main
Hello, World!

在 Windows 上,就要输入命令 .\main.exe 而不是 ./main

> rustc main.rs
> .\main.exe
Hello, World!

而不论所在操作系统为何,字符串 Hello, World! 都应打印到终端。而若没有看到这个输出,那么请回到安装小节的 “问题排除” 部分获取帮助。

如确实打印出了 Hello, World!,那么恭喜你!你已正式编写除了一个 Rust 程序了。那就让你成为了一名 Rust 程序员了 -- 欢迎!

Rust 程序解析

来仔细回顾一下刚才在 “Hello World” 程序中发生了什么。这是谜团中第一部分:

fn main() {

}

这些行定义了 Rust 中的一个函数。这个 main 函数比较特殊:在每个可执行的 Rust 程序中,他总是第一个开始运行的代码。这第一行声明了一个名为 main 的、没有参数且不返回任何值的参数。若函数有参数,那么参数就应位处圆括号()内部。

还有就是,请注意函数体是包裹在花括号{}中的。Rust 要求将全部函数体都用花括号包裹起来。将开头的花括号与函数声明放在同一行,并在二者之间加上一个空格,是良好的代码风格。

若想要在多个 Rust 项目之间保持一种标准的编码风格,那么就可以使用一个名为 rustfmt 的自动格式化工具,来以一种特定样式对代码进行格式化。与 rustc 一样Rust 团队已将此工具包含在标准的 Rust 发布中,因此在你的电脑上就应该已经有了这个格式化工具了!请查看在线文档了解更多详情。

在这个main 函数里头,是下面的代码:

println!("Hello, World!");

这行代码完成了此小程序的全部工作:他将文字打印到屏幕。这里有四个需要注意的重要细节。

首先Rust 编码风格是缩进四个空格,而非一个制表符;

其次,println! 调用了一个 Rust 的宏a Rust macro。若他调用的是个函数那么就应输入 println (是不带 ! 的)。在后续的第 19 章,将详细讨论 Rust 的宏。而现在,则只需知道 ! 的使用表示是在调用某个宏而不是普通函数,同时宏不会总是遵循与函数同样的规则;

第三,就是看到的 Hello, World! 这个字符串了。这里时将此字符串作为参数,传递给 println! 的,且这个字符串是被打印到屏幕上的;

最后,这行语句是以分号(;结束的这表示该表达式结束同时下一表达式已准备好开始。Rust 代码的多数行,都是以分号结束的。

编译和运行是分开的步骤

这里刚刚运行了一个新近创建出的程序,那么来检视一下该过程的每个步骤。

在运行某个 Rust 程序之前,必须要通过敲入 rustc 命令并将源代码文件名字,作为rustc的参数加以传入,这样来使用 Rust 编译器对其进行编译,像下面这样:

$ rustc main.rs

若你有 C 或 C++ 的背景知识,那么就会注意到这与 gccclang 类似。在成功编译后Rust 就会输出一个二进制可执行文件。

在 Linux、macOS 和 Windows 上的 PowerShell 之上,就可以通过在 shell 中敲入 ls 看到这个可执行文件。在 Linux 与 macOS 上,将看到下面这两个文件。而在 Windows 上的 PowerShell 中,则会看到与使用 CMD 一样的以下三个文件。

$ ls
main    main.rs

在 Windows 的 CMD 中,就应输入下面的东西:

> dir /B %= 这里的 /B 选项表示只显示文件名 =%
main.exe
main.pdb
main.rs

这显示了带有 .rs 扩展名的源代码文件、那个可执行文件Windows 上的 main.exe,对于其他平台则是 main),以及,在使用 Windows 时,一个包含了调试信息的、带有 .pdb 扩展名的文件。从此处,就像下面这样来运行这里的 mainmain.exe

$ ./main # 或在 Windows 上的 .\main.exe

若这里的 main.rs 就是那个 “Hello, World!” 程序,那么这行命令就会将 Hello, World! 打印到你的终端了。

若你对某门动态语言,诸如 Ruby、Python 或者 JavaScript 更为熟悉那么可能就不习惯于将编译和运行某个程序作为分开的步骤。Rust 是门 提前编译 语言an ahead-of-time compiled language这意味着可对程序进行编译而将可执行文件交给他人他们可在未安装 Rust 的情况下运行编译好的可执行文件。而若将某个 .rb.py,或者 .js 文件交给某人时,他们就需要安装好相应的 Ruby、Python 或 JavaScript 实现。不过在这些语言中,仅需一个命令来编译和运行他们的程序。在编程语言设计中,每件事都有所取舍。

对于简单的程序来说,用 rustc 编译就足够了,但随着项目的成长,就希望对所有选项进行管理,并令到代码分享更为简便。接下来,就要介绍 Cargo 工具了,这工具将帮助我们编写出实用的 Rust 程序。

你好Cargo

Cargo 是 Rust 的构建系统和包管理器。由于 Cargo 处理了很多任务,诸如构建代码、下载代码所依赖的库,以及这些库的构建等等,因此绝大多数 Rust 公民都使用这个工具,来管理他们的 Rust 项目。我们把这些库叫做代码需要依赖we call the libraries that your code needs dependencies)。)

对于最简单的那些 Rust 程序,比如才写的那个,是没有任何依赖的。因此若使用 Cargo 来构建这个 Hello, World! 项目,那么就只会用到 Cargo 处理代码构建的部分。而随着更为复杂 Rust 程序的编写,就会添加依赖,而在开始一个用到 Cargo 的项目时,完成依赖添加就会容易得多。

由于广大 Rust 项目都用到了 Cargo本书其余部分就假定了也使用 Cargo。若使用了在 安装 小节中提到的官方安装器进行的 Rust 安装那么Cargo就已与 Rust 一起安装好了。而若是以其他方式安装的 Rust那么就要通过在终端中敲入下面的命令来检查 Cargo 是否已安装妥当:

$ cargo --version

若能看到版本号,那么就有了这个工具!而若看到错误,诸如 command not found,就请查看你的安装方式的文档,找到怎样单独安装 Cargo 的方法。

使用 Cargo 创建项目

下面来使用 Cargo 创建一个新项目,并看看与原先的 “Hello, World!” 项目有何不同。现在导航至 projects 目录(或确定下来的保存代码的其他地方)。然后不论在那个操作系统之上,运行下面的命令:

$ cargo new hello_cargo
$ cd hello_cargo

这第一个命令创建出了一个新的名为 hello_cargo 目录。这里就已将项目命名为了 hello_cargo,然后 Cargo 将其文件创建在了同名的目录里面。

进入到 hello_cargo 目录并列出那些文件。就会看到 Cargo 已经为我们生成了两个文件和一个目录:一个 Cargo.toml文件与一个里头有着 main.rs 文件的 src 目录。

cargo new 还初始化了一个新的、带有 .gitignore 文件的 Git 代码仓库。若是在一个既有的 Git 代码仓库运行的 cargo new,那么就不会生成那些 Git 文件;通过运用 cargo new --vcs=git 可重写此行为。

注意Git 是种常用的版本控制系统。可通过上面的 --vcs 命令行参数,让 cargo new 使用其他版本控制系统或不使用版本控制系统。请运行 cargo new --help命令来查看所有可用选项。

文件名:Cargo.toml

[package]
name = "hello_cargo"
version = "0.1.0"
edition = '2021'

[dependencies]

清单 1-2cargo new 所生成的 Cargo.toml 的内容

该文件是 TOML Tom's Obvious, Minimal Language 格式的,这是 Cargo 的配置格式。

该文件的第一行, [package],是个小节标题,表示接下来的语句是在对一个包进行配置。随着往这个文件添加越来越多的信息,就会添加其他小节。

接下来的三行,对 Cargo 用于编译程序所需的信息进行了配置:项目名称、版本号及要使用的 Rust 版本。在 附录 E 中会讲到这个 edition 关键字。

Cargo.toml 的最后一行,[dependencies],是要列出项目全部依赖小节开始的地方。在 Rust 中,代码包被称为 包裹crates。此项目无需任何其他包裹,在第 2 章中的头一个项目,就会用到依赖包裹,因此在那时就会用到这个依赖小节。

现在打开 src/main.rs 然后看看:

文件名:src/main.rs

fn main() {
    println! ("Hello, World!");
}

Cargo 以及为我们生成了一个 “Hello, World!” 的程序,这个自动生成的程序就跟之前在清单 1-1 中的一样!到现在,先前的项目与这个 Cargo 生成的项目的不同之处,就是 Cargo 是将代码放在那个 src 目录中的,同时在顶层目录还有了一个 Cargo.toml 配置文件。

Cargo 希望那些源代码文件,存留在 src 目录里头。而顶层的项目目录,只用于 README 文件、许可证信息、配置文件及其他与代码无关的东西。使用 Cargo 有助于对项目的组织。一切都有了个地方且一切都在各自的地方there's a place for everything, and everything is in its place

若没有使用 Cargo 来开始项目,就如同先前在 “Hello, World!” 项目中所做那样,那么仍旧可使用 Cargo 将其转换为一个项目。将项目代码移入到 src 目录并创建出一个适当的 Cargo.toml 文件来:

$ cd hello_world
$ mkdir src
$ mv main.rs src/
$ cargo init

构建和运行一个 Cargo 项目

现在来看看在使用 Cargo 来构建和运行那个 “Hello, World!” 程序有什么不同之处!在 hello_cargo 目录,通过敲入下面的命令,来构建该项目:

$ cargo build                                                                   ✔ 
   Compiling hello_cargo v0.1.0 (/home/peng/rust-lang/projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.45s

此命令创建出在 target/debug/hello_cargo(或 Windows 上的target\debug\hello_cargo.exe)中,而非当前目录下的一个可执行文件。可使用下面这个命令运行那个可执行程序:

$ ./target/debug/hello_cargo # 或者在 Windows 上的 .\target\debug\hello_cargo.exe
Hello, world!

若一切顺利,那么 Hello, World! 就会被打印到终端。首次运行 cargo build,还会造成 Cargo 在顶层目录创建一个新文件:Cargo.lock。该文件会跟踪项目中各个依赖的精确版本。由于这个项目没有依赖因此该文件有些稀疏。绝无必要手动修改此文件Cargo 会为我们管理他的内容。

文件名:Cargo.lock

# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "hello_cargo"
version = "0.1.0"

清单 1-3, Cargo.lock

这里刚刚使用 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 曾在编译 hello_cargo 的输出。Cargo 发现这些文件并未发生改变,因此他就运行了那个二进制文件。若曾修改过源代码,那么 Cargo 就会在运行这个项目之前,重新构建该项目,从而会看到这样的输出:

$ cargo run                                                                                        ✔ 
   Compiling hello_cargo v0.1.0 (/home/peng/rust-lang/projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.43s
     Running `target/debug/hello_cargo`
Hello, Cargo!

Cargo 还提供了一个叫做 cargo check 的命令。此命令会对代码进行快速检查,以确保代码可被编译,但该命令不会产生出可执行程序:

$ cargo check                                                                                      ✔ 
   Checking hello_cargo v0.1.0 (/home/peng/rust-lang/projects/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.35s

这里为何不要一个可执行文件呢?通常,由于 cargo check 跳过了产生出可执行程序的步骤,因此他要比 cargo build 快得多。但在编写代码时,要持续检查已完成的工作时,那么 cargo check 的使用,就会加速工作流程!由于这个原因,许多的 Rust 公民,都会在编写他们的程序时,定期运行 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

对于那些简单项目,相比于使用 rustcCargo 并未提供到很多价值然而在程序变得愈加错综复杂时他就会证明他的价值了。对于那些由多个代码箱crates 构成的复杂项目,让 Cargo 来对构建进行协调,就要容易得多。

即使这个hello_cargo 项目如此,此刻也用到了将在接下来的 Rust 编程生涯中会用到的真正工具。事实上,对于在任何既有的 Rust 项目,都应使用下面这些命令,使用 Git 来检出代码,然后前往到项目目录,进而加以构建:

$ git clone example.org/someproject
$ cd someproject
$ cargo build

更多有关 Cargo 的信息,请查看看Cargo 文档