Rust实战
上QQ阅读APP看书,第一时间看更新

1.3 Rust初体验

在本节内容中,你将初次体验Rust。我们首先要了解如何使用编译器,然后会快速编写一个程序。在接下来的章节中,我们会写一个完整的项目。


注意 要安装Rust,需要使用官方提供的安装器(installer)进行安装。请登录Rust官方网站并下载。


1.3.1 直通“Hello, world!”

大多数程序员在接触一门新的语言时,要做的第一件事就是学习如何在控制台上输出“Hello,world!”。接下来你也需要这样做。在遇到令人讨厌的语法错误之前,你要先验证所有环境是否已经准备就绪。

如果你使用的是Windows系统,在安装完Rust以后,请通过开始菜单打开命令提示符窗口。然后请执行以下命令:

C:\> cd %TMP%

如果你使用的是Linux或macOS系统,请打开一个终端窗口。打开后,请执行以下命令:

$ cd $TMP

从这里开始,所有操作系统的命令应该是相同的。如果你的Rust环境安装正确,那么执行以下3个命令,将会在屏幕上输出“Hello,world!”(以及一些其他输出)。

$ cargo new hello
$ cd hello
$ cargo run

这是在Windows上执行cmd.exe进入命令行提示符窗口以后,执行整个会话过程的示例:

C:\> cd %TMP%
C:\Users\Tim\AppData\Local\Temp\> cargo new hello
Created binary (application) 'hello' project
C:\Users\Tim\AppData\Local\Temp\> cd hello
C:\Users\Tim\AppData\Local\Temp\hello\> cargo run
Compiling hello v0.1.0 (file:///C:/Users/Tim/AppData/Local/Temp/hello)
Finished debug [unoptimized + debuginfo] target(s) in 0.32s
Running 'target\debug\hello.exe'
Hello, world!

类似地,在Linux或macOS上,你的控制台上显示的信息应该像下面这样:

$ cd $TMP
$ cargo new hello
Created binary (application) 'hello' package
$ cd hello
$ cargo run
Compiling hello v0.1.0 (/tsm/hello)
Finished dev [unoptimized + debuginfo] target(s) in 0.26s
Running 'target/debug/hello'
Hello, world!

如果你走到了这一步,那就太棒了!你已经运行了自己的第一段Rust代码,而且并不需要编写多少Rust代码。接下来,让我们来看看在整个过程中都发生了什么。

Rust的cargo工具既提供了一个构建系统,又提供了包管理器。这意味着cargo知道如何将Rust代码转换成可执行的二进制文件,同时能够管理项目依赖包的下载和编译的过程。

cargo new会遵照标准模板创建一个项目。执行tree命令就能清楚地看到在执行cargo new之后默认的项目目录结构,以及创建出的那些文件:

$ tree hello
hello
├──Cargo.toml
└──src
└──main.rs
1 directory, 2 files

用cargo创建出来的所有Rust项目有着相同的结构。在项目的根目录中,名为Cargo.toml的文件描述了项目的元数据,例如项目的名称、项目的版本号及其依赖项。源代码保存在src目录中。Rust源文件使用.rs作为它的文件扩展名。要想查看cargo new创建出来的那些文件,可以使用tree命令。

接下来,你要执行的命令是cargo run。这个操作对你来说很容易掌握,然而cargo实际上做的工作比你以为的要多得多。你要求cargo去运行此项目。在调用此命令时,还没有任何实际可执行的程序文件存在,它决定使用调试模式(debug mode)来为你编译代码,这样可以提供最大化的错误信息(error information)。碰巧的是,src/main.rs文件总是会包含一个“Hello,world!”作为初始代码。编译的结果是一个名为hello(或hello.exe)的文件。紧接着它会执行这个文件,并把执行结果输出到屏幕上。

执行cargo run以后,项目还会增加一些新的文件。现在,在项目的根目录中会有一个Cargo.lock文件,还有一个target/目录。这两者都是由cargo管理的。因为它们都是编译过程中的产物,所以可以不予理会。Cargo.lock文件指定了所有依赖项的具体版本号,所以程序总会使用同样的方式,可靠地构建将来的程序版本,直到Cargo.toml的内容被修改才会改变这种构建的方式。

在执行cargo run来编译项目以后,再次执行tree命令来查看新的目录结构:

$ tree --dirsfirst hello
hello
├──src
│  └── main.rs
├──target
│  └── debug
│      ├──build
│      ├──deps
│      ├──examples
│      ├──native
│      └──hello
├──Cargo.lock
└──Cargo.toml

至此,所有步骤能正常运行了,很好!我们已经走捷径直通“Hello, world!”,接下来让我们走一条稍远点儿的路,再次到达这里。

1.3.2 第一个Rust程序

作为第一个Rust程序,我们想编写代码,用于输出以下文本信息:

Hello, world!
Grüß Gott!
ハロー・ワールド

在本书的Rust之旅中,你应该已经见过此输出内容的第一行了。另外的两行是为了展示出Rust的以下特点:易用的迭代和内置对Unicode的支持。与1.3.1节中的程序一样,在这个程序中我们会使用cargo。具体步骤如下。

(1)打开控制台窗口。

(2)如果是在Windows上,就执行cd %TMP%;如果在其他操作系统上,就执行cd $TMP。

(3)执行cargo new hello2命令,创建一个新项目。

(4)执行cd hello2命令,移动到此项目的根目录中。

(5)在一个文本编辑器中打开src/main.rs文件。

(6)用清单1.1中的内容替换该文件中的文本。

清单1.1的源代码参见ch1/ch1-hello2/src/hello2.rs。

清单1.1 用3种语言说“Hello,world!”

 1 fn greet_world() {
2     println!("Hello, world!");    ⇽---  这里的第一个感叹号表示这是一个宏,这个我们稍后会讨论。
3     let southern_germany = "Grüß Gott!";    ⇽---  Rust中的赋值,更恰当的说法叫作变量绑定,使用let关键字。
4     let japan = "ハロー・ワールド";    ⇽---  对Unicode的支持,是“开箱即用”的。
5     let regions = [southern_germany, japan];    ⇽---  数组字面量使用方括号。
6     for region in regions.iter() {    ⇽---  很多类型都有iter()方法,此方法会返回一个迭代器。
7             println!("{}", &region);    ⇽---  此处的和符号(&)表示“借用”region的值,用于只读的访问。
8     }
9 }
10
11 fn main() {
12     greet_world();    ⇽---  调用一个函数。要注意紧跟在函数名后面的圆括号。
13 }

现在,代码更新了,只需在hello2/目录里执行cargo run,你应该能看到这3个问候语(它们出现在cargo自己的一些输出的后面),如下所示:

$ cargo run
Compiling hello2 v0.1.0 (/path/to/ch1/ch1-hello2)
Finished dev [unoptimized + debuginfo] target(s) in 0.95s
Running 'target/debug/hello2'
Hello, world!
Grüß Gott!
ハロー・ワールド

让我们花点儿时间来谈谈清单1.1中一些有意思的语言元素。

在这个例子里,你可能最先注意到的就是,Rust中的字符串能够包含许多不同的字符。在Rust中,字符串能够确保是有效的UTF-8编码。这意味着你可以相对轻松地使用非英语的语言。

有一个字符看起来会有点儿奇怪,就是println后面的感叹号。如果你用Ruby编写过程序,可能就会习惯性地认为它表示一个破坏性的操作。然而在Rust中,它表示的是使用一个。就现在来讲,你可以把宏看作一类奇特的函数,其提供了避免“样板代码”(boilerplate code)的能力。对于本例中的println!宏来说,实际上它在底层进行了大量的类型检测工作,所以才能把任意的数据类型输出到屏幕上。