hello_world
第一个 Rust 程序:
新建完文件夹后,新建一个后缀名为 .rs
的文件(Rust 源文件总是以 .rs
拓展名结尾)
新建后编写下面的代码:
1 | fn main() { |
保存文件,使用命令运行:
1 | rustc main.rs |
Rust 程序分析:
Rust 和 C 语言类似,需要有一个 main
函数。
1 | fn main() { |
官方文档中的描述:
main
函数是一个特殊的函数:在可执行的 Rust 程序中,它总是最先运行的代码。第一行代码声明了一个叫做main
的函数,它没有参数也没有返回值。如果有参数的话,它们的名称应该出现在小括号()
中。函数体被包裹在
{}
中。Rust 要求所有函数体都要用花括号包裹起来。一般来说,将左花括号与函数声明置于同一行并以空格分隔,是良好的代码风格。
在 main
函数中有下面的代码:
1 | println!("Hello, world"); |
需要注意的有:
- 首先,Rust 的缩进风格是使用 4 个空格,而不是 1 个制表符(tab)
println!
调用了一个 Rust 宏(macro)。如果是调用函数,应该输入println
(没有 ! )。**==当看到符号!
的时候,就意味着调用的是宏而不是普通函数,并且宏并不总是遵循与函数相同的规则==**"hello, world"
是一个字符串。把这个字符串作为一个参数传递给println!
,字符串将被打印到屏幕上。- 大部分
Rust
代码行以分号结尾。
在 Rust 中,编译个运行时彼此独立的步骤
在运行Rust
程序之前,必须使用 Rust
编译器编译它。即输入 rustc
命令并传入源代码名称:
1 | rustc main.rs |
与 C 语言类型,Rust 编译成功后会输出一个二进制的可执行文件
在 Linux 和 macOS,可以看到两个文件。在 Windows PowerShell 中,可以看到的三个文件。.exe .pdb .rs
其中拓展名为*.pdb
*的文件是一个包含调试信息的文件。
==Rust 是一种 预编译静态类型(ahead-of-time compiled)语言,这意味着你可以编译程序,并将可执行文件送给其他人,他们甚至不需要安装 Rust 就可以运行。==
Cargo
可参考:Rust 圣经-初始Cargo
定义:(来自官方文档)
Cargo 是 Rust 的构建系统和包管理器。大多数 Rustacean 们使用 Cargo 来管理他们的 Rust 项目,因为它可以为你处理很多任务,比如构建代码、下载依赖库并编译这些库。(我们把代码所需要的库叫做 依赖(dependencies))。
在编写一些较复杂的 Rust 程序时,需要添加依赖项,如果使用 Cargo 启动项目,则添加依赖项将更加容易。
使用 Cargo 创建项目
1 | cargo new hello_cargo |
第一行命令新建了一个名为*hello_cargo
的目录和项目。我们将项目命名为hello_cargo
*,同时 Cargo 在一个同名目录中创建项目文件
进入 hello_cargo 目录并列出文件。可以看到 Cargo 生成了两个文件和一个目录:一个 Cargo.toml 文件,一个 src
目录,以及位于src
目录中的*main.rs
文件。*
这也会在 hello_cargo 目录初始化了一个 git 仓库,以及一个 .gitignore 文件。如果在一个已经存在的 git 仓库中运行 cargo new
,则这些 git 相关文件则不会生成;可以通过运行 cargo new --vcs=git
来覆盖这些行为。
Cargo.toml
这个文件使用 TOML*(Tom’s Obvious,Minimal Language)*格式,这是 Cargo 配置文件的格式。
1 | [package] |
上面的示例:*cargo new
*命令生成的 *Cargo.toml
*的内容
- 第一行,
[package]
,是一个片段(section)标题,表明下面的语句用来配置一个包。随着我们在这个文件增加更多的信息,还将增加其他片段(section)。 - 接下来的三行设置了**
Cargo
编译程序所需的配置:**- 项目名称
- 项目版本
- 要使用的 Rust 版本
- 最后一行,**
[dependencies]
,是罗列项目以来的片段的开始。在 Rust 中,代码包被称为*crates
***,这个项目不需要其他的crate
,不过在后面会用到依赖,那是会用得上这个片段。
这里会介绍edition
的值。
src 目录
打开 ***src
目录,可以看到一个main.rs
***文件:
1 | fn main() { |
Cargo 会自动生成一个”Hello ,world!”程序
目前为止,我们自己的项目与使用 Cargo 生成项目的区别是 Cargo 将代码放到*src
目录中,同时项目根目录包含一个Cargo.toml
*配置文件
Cargo 期望源文件存放在 src 目录中。项目根目录只存放 README、license 信息、配置文件和其他跟代码无关的文件。使用 Cargo 帮助你保持项目干净整洁,一切井井有条。
如果没有使用 Cargo 开始项目,我们可以将其转化成为一个 Cargo
项目,将代码放入*src
目录,并创建一个合适的Cargo.toml
*文件。
构建&运行 Cargo 项目
通过 Cargo 构建和运行”Hello, world!” 程序有什么不同?
Cargo build
在 hello_cargo 目录下,输入下面的命令来构建项目:
1 | cargo build |
上述命令会创建一个可执行文件target/debug/hello_cargo.exe
,而不是放在目前目录下。由于默认的构建方法是调试构建(debug building),Cargo 会将可执行文件放在名为 *debug
*的目录中。**可通过这个命令运行可执行文件:
1 | .\target\debug\hello_cargo.exe |
首次运行cargo build
时,也会使Cargo
在项目根目录创建一个新文件*Cargo.lock
*。这个文件记录项目以来的实际版本因为这个项目没有以来,所以其中内容较少。
Cargo run
刚刚使用cargo build
构建了项目,并使用.\target\debug\hello_cargo
来运行了程序,也可以使用cargo run
在一个命令中同时编译并运行生成的可执行文件:
1 | cargo run |
Cargo check
Cargo 还提供了一个叫cargo check
的命令。**cargo check
命令可以快速检查代码确保其可以编译,但并不产生可执行文件:**
1 | cargo check |
小结:
- 可以使用
cargo new
创建项目。 - 可以使用
cargo build
构建项目。 - 可以使用
cargo run
一步构建并运行项目。 - 可以使用
cargo check
在不生成二进制文件的情况下构建项目来检查错误。 - 有别于将构建结果放在与源码相同的目录,Cargo 会将其放到 target/debug 目录。
使用 Cargo 的一个额外的优点是,不管你使用什么操作系统,其命令都是一样的。所以从现在开始本书将不再为 Linux 和 macOS 以及 Windows 提供相应的命令。
发布(release)构建
当项目最终准备好发布时,可以使用cargo build --release
来优化编译项目。这会在*target/release
*而不是target/debug
生成可执行文件。这些优化可以让 Rust 代码运行的更快,不过启用这些优化也需要消耗更长的编译时间。这也就是为什么会有两种不同的配置:**一种是为了开发,需要经常快速重新构建;另一种是为了用户构建最终程序,它们不会经常重新构建,并且希望程序运行得越快越好。
如果在测试代码的运行时间,确保运行cargo build --release
并使用*target/release
*下的可执行文件进行测试。
不仅仅是 Hello world
多国语言的“hello world”
使用 VSCode 新建一个 world_hello 工程,进入 main.rs
文件。
1 | fn greet_world() { |
首先,Rust 原生支持 UTF-8 编码的字符串,这意味着你可以很容易的使用世界各国文字作为字符串内容。
其次,关注下 println
后面的 !
,是在 Rust 中的 宏
操作。目前可认为是一种特殊类型函数。
对于 println
来说,是使用 {}
作为输出占位符,因为 Rust 在底层帮我们做了大量工作,会自动识别输出数据的类型,例如当前例子,会识别为 String
类型。
最后,和其它语言不同,Rust 的集合类型不能直接进行循环,需要变成迭代器(这里是通过 .iter()
方法),才能用于迭代循环。在目前来看,你会觉得这一点好像挺麻烦,不急,以后就知道这么做的好处所在。
实际上这段代码可以简写,在 2021 edition 及以后,支持直接写
for region in regions
,原因会在迭代器章节的开头提到,是因为 for 隐式地将 regions 转换成迭代器。
至于函数声明、调用、数组的使用,和其它语言没什么区别.
Rust 初印象
1 | fn main() { |
上面代码中,值得注意的 Rust 特性有:
- 控制流:
for
和continue
连在一起使用,实现循环控制。 - 方法语法:由于 Rust 没有继承,因此 Rust 不是传统意义上的面向对象语言,但是它却从
OO
语言那里偷师了方法的使用record.trim()
,record.split(',')
等。 - 高阶函数编程:函数可以作为参数也能作为返回值,例如
.map(|field| field.trim())
,这里map
方法中使用闭包函数作为参数,也可以称呼为匿名函数
、lambda 函数
。 - 类型标注:
if let Ok(length) = fields[1].parse::<f32>()
,通过::<f32>
的使用,告诉编译器length
是一个f32
类型的浮点数。这种类型标注不是很常用,但是在编译器无法推断出你的数据类型时,就很有用了。 - 条件编译:
if cfg!(debug_assertions)
,说明紧跟其后的输出(打印)只在debug
模式下生效。 - 隐式返回:Rust 提供了
return
关键字用于函数返回,但是在很多时候,我们可以省略它。因为 Rust 是 基于表达式的语言。
在终端中运行上述代码时,会看到很多 debug: ...
的输出,上面有讲,这些都是 条件编译
的输出
cargo run
默认是运行 debug
模式。因此想要消灭那些 debug:
输出,需要更改为其它模式,其中最常用的模式就是 --release
也就是生产发布的模式。