此笔记为在学习Rust圣经《The Rust Programming Language》时记录的笔记,为基础学习

中文版书籍:Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (kaisery.github.io)

安装过程不再赘述,可直接参考Rust官方网站的安装过程

官方网站地址:Rust Programming Language (rust-lang.org)

本人开发环境为:

  • ManjaroWSL2
  • Clion

学习Rust纯属为个人一时兴起,本笔记随时弃坑。

一个简单的Rust程序

1
2
3
4
5
6
7
8
9
10
11
use std::io;

fn main() {
println!("Guess the number!");
println!("Please input your guess:");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line.");
println!("Your guessed: {guess}");
}

运行结果:

image-20221205012227933

这个程序的逻辑很简单,就是处理用户的输入,把用户的输入进行输出。

但是这个程序拥有了Rust程序的基本结构,我们从上直下解析这个程序:

1
use std::io;

为了获取的用户输入,我们需要导入输入输出库(input / output),即io库。io库来自于标准库,即std。这个操作类似于c语言的include,或者是Java的import

在默认的情况下,Rust会自动导入部分标准库到每个程序作用域,这部分被称为预导入,若我们所需要用的不在其中,则需要我们手动使用use显式导入。

1
fn main() {

fn用于声明新函数,main函数是程序的入口点。

1
2
println!("Guess the number!");
println!("Please input your guess:");

在这里println!用于在屏幕中打印字符串。

**注意:**它并不是一个函数,而是一个宏,!的基本上都是宏。

使用变量存储值

1
let mut guess = String::new();

在这里我们创建了一个空的字符串变量用于存储输入,我们注意到,与很多常见的编程语言不同,我们申明用了两个关键词let mut

这是因为Rust中,变量默认是不可变的,因此我们如果使用let guess的话,会使得guess类似其他编程语言中的常量一样,不可变,因此我们还要通过mut关键词使得变量可变。

=是在告诉Rust,我们想将某个值绑定在变量上。因此我们将String::new()的结果绑定到了guess上,它的结果是一个String的新实例。

::new()注意,这里的new()并不是String的特定函数,而是一个跟Java静态方法一样的关联函数。在这里::就表明了new是String的关联函数。new函数是创建类型实例的惯用函数

接受输入与异常处理

1
2
3
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line.");

现在我们调用一开始导入的io库中的函数接受用户的输入,我们使用了stdin函数,stdin函数返回了一个std::io::Stdin的实例,这是终端标准输入句柄类型。

.read_line(&mut guess)调用了read_line方法,从句柄中获取到用户的一行输入,并将其追加到参数中,因为我们传入的参数应该是可变的。

&代表了引用,它使得程序中不同处的代码可以访问同一处的数据。它与变量相同,默认都是不可变,因此我们需要将其写成&mut guess以使其可变。

.expect("Failed to read line.")的作用就是异常处理。read_line会返回一个Result类型,Result是一个枚举类型,其中包含了OkErr,分别代表操作成功与失败。

Result类型的实例拥有一个expect方法,若Result实例的值为Err,expect方法会导致程序崩溃,并显示expect中传入的值,若Result实例的值为Ok,则会原样返回Ok中的值。

占位符打印值

1
println!("Your guessed: {guess}");

这行代码打印了保存在guess变量中用户输入的值。里面的{}为一个预留给变量类的占位符号,我们除了把变量直接写在{}中,我们还可以在双引号的外面按顺序写值,这个跟c类似,应该不陌生:

1
println!("x = {}, y = {}", x, y);

第一个占位符使用第一个值,第二个占用符使用第二个值…以此类推。

引入Crate实现更多功能

Crate是一个Rust代码包,是Rust在编译时最小的代码单位,它有两种形式,二进制Crate库Crate

二进制Crate是一个可执行的程序,比如我们写的一个简单的Rust程序。它们必须要一个main函数来确定程序的入口;

库Crate与其他编程语言中的library概念一致,包含了可以被其他程序使用的代码,它们提供了一些诸如函数之类的东西,使得其他项目能够使用。它们并不能自执行。


上面我们介绍了Crate,下面我们要用到一个外部的库Crate来完善我们的程序。

一个简单的Rust程序编写了一个让用户输入,然后程序输出用户的输入的程序,从println!的内容我们可以看出我们并不是想做出一个这么简单的程序,我们是想让用户来猜我们的神秘数字。

为了生成一个随机的神秘数字,我们需要用到随机数,Rust标准库中尚未包含随机数的功能,但是我们可以引入包含随机数功能的rand库Crate。

我们需要去修改Cargo.toml文件,引入一个rand引用,现在我们将下面一行添加到[dependencies]下:

1
rand = "0.8.3"

现在我们不修改任何代码,构建项目,我们会发现Cargo帮我们下载了rand和它依赖的一系列库Crate,Cargo会帮我们检测需要新增的Crate和我们是否修改了代码,并帮我完成下载与构建。

Cargo.toml中的依赖版本信息,并不是指定某一个版本,而是一个区间——一个大于等于0.8.3且小于0.9.0的版本。

为了我们的构建结果是可以被任何人复刻的,Cargo.lock文件会帮助每个人的Cargo只会使用指定的依赖版本,除非手动去指定其他版本。我们会将Cargo.lock纳入版本控制中,以保证每个人都可以复刻这个构建结果。

确实需要更新Crate的版本时,我们还可以通过Cargo update忽视Cargo.lock文件,并重新计算符合Cargo.toml声明的最新版本,将其更新且写入到Cargo.lock文件中。


让我们开始使用rand来生成一个随机数字,修改我们的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use std::io;
use rand::Rng;

fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
}

我们依旧是从上往下看我们新增的代码:

1
use rand::Rng;

这里导入了属于rand包里的一个trait——Rngtrait概念类似于其他语言中的接口,里面会定义很多方法。

1
let secret_number = rand::thread_rng().gen_range(1..=100);

这里调用了rand::thread_rng()函数,提供