2.1 单次猜测
代码
1 | use std::io; |
解析
std::io
代表调用标准库std
中的io
库,使用use
命令Rust中所有的变量都是默认不可变的(immutable),例如
1
2let foo = 1;
let bar = foo;如果希望变量可变,则需要加上
mut
关键字在Rust中,字符串类型为
String
,包含在标准库中,而String::new()
返回字符串的一个新的实例,默认为UTF8编码的空的字符串- 这里的
::
和C++的含义不同,代表new()
是String
类型的一个关联函数, - 关联函数是针对类型的函数,而不是针对特定实例的函数
- 这里的
函数
stdin()
会返回Stdin
类型的一个实例,而方法read_line()
会获取用户的输入,并将输入存到字符串guess
中- 该方法要求字符串必须是mutable的,因此需要
&mut
关键字 - 其中
&
代表该参数是一个引用,引用的目的是在代码的不同地方访问同一块数据,即代表&mut guess
中的guess
和前面定义的guess
指向同一块数据 - 此处方法的参数是要通过引用来传递的
- 该方法要求字符串必须是mutable的,因此需要
方法
read_line()
的返回值为枚举类型io::Result<usize>
,若读取成功,将返回Ok
且其包含结果值;否则返回Err
并附带失败原因。对于该枚举类型,其还定义了一些方法,例如此处使用的expect()
- 若该实例
io::Result<usize>
返回Err
,则expect()
方法会中断当前程序,并显示传入的字符串信息 - 否则,
expect()
方法会提取Ok
中附加的值,并将该值作为结果返回给用户
- 若该实例
最后一行
println!()
中的{}
为占位符,类似Python中的print()
,会被后面的变量替换作为输出
2.2 生成神秘数字
Rust的标准库中没有提供生成随机数的功能,需要借助名为Rand
的crate
Rust中的crate分为两种
- 二进制crate:可独立运行
- library crate:不能独立运行,即需要被调用,和项目一起生成二进制文件
为了添加Rand
这个crate的依赖,我们需要编辑Cargo.toml
文件,并添加在````[dependencies]```模块下,例如
1 | [dependencies] |
0.3.14
为版本号如果写作
"^0.3.14"
,则^
代表任何与公共API兼容的版本都可以
这个crate将在cargo build
的时候进行下载,包括相关的依赖库
为了保证代码的复现性,在首次进行build之后,会生成Cargo.lock
文件,它会记录第一次build使对应的crate的版本信息(对应的小版本号的最新版,如0.3.23
),即便后续修改Cargo.toml
文件中对应库的版本再进行build操作,也不会更新库的版本
换言之,只要Cargo.lock
文件存在,Cargo将一直使用该文件中的库版本,直到运行cargo update
命令,才会重新根据Cargo.toml
中的版本来更新Cargo.lock
中编译用的版本
为了生成随机数,我们使用
1 | use rand::Rng; |
其中Rng
是rand
这个库里的trait,类似其它语言中API,上面会定义很多方法
Rng
这个trait里面定义了很多随机数生成器的方法
为了生成随机数,我们使用语句
1 | let secrete_number = rand::thread_rng().gen_range(1..101); |
其中
- 函数
thread_rng()
返回的是ThreadRng
类型,该类型是一个随机数生成器,其位于本地线程空间,并通过操作系统来获取随机数种子 gen_range()
是Rng
这个trait定义的方法,会生成范围[1, 101)内的一个随机整数
综上,我们修改后的最新完整代码为
1 | use std::io; |
2.3 比大小
Rust在赋值时可以像C++的auto
语句一样进行强类型推断,例如let mut guess
可以通过右面表达式推断guess
的静态类型要被声明成为String
。
至于随机数生成部分的代码,由于i32
,u32
,u64
均满足该条件,若无更多信息被指定来用于类型推断,否则将默认指定为i32
类型
为了比较secrete_number
和guess
,我们需要先进行类型转换,即
1 | let guess: u32 = guess.trim().parse().expect("请输入一个合法的数字!"); |
trim()
方法用于去掉字符串两端的空白,包括tab和回车parse()
方法用于将其解析成u32
类型,这个u32
由等号前的对应关键字来指定,该解析可能会失败,例如传入xyz
的时候- 为了处理这种情况,
parse()
方法会返回Result
这个枚举类型 - 和前面的
read_line()
方法类似,我们可以调用该返回类型的expect()
方法来报错
- 为了处理这种情况,
这里的
guess: u32
是声明了一个新的guess
且为u32
类型,其会进行shadow操作,即它会覆盖前面出现的字符串类型的guess
,且在其之后,程序调用的guess都是这个新定义的guess
1 | match guess.cmp(&secrete_number) { |
这里引入名为std::cmp::ordering
这一枚举类型,其有三个变体Less
,Greater
,Equal
此外,match
表达式后面跟多个arm(分支),如果match
后面表达式的值可以和某个arm匹配,则执行对应语句
在有这一比较的语句出现之后,编译器会将secrete_number
解释称对应的u32
类型,而不再是i32
类型
最终的完整代码为(注意开头的use std::cmp::Ordering;
)
1 | use std::io; |
2.4 允许多次猜测
为了允许多次猜测直至用户才对,需要实现一个无限循环,这将借助loop
语句和break
来跳出循环:
1 | use std::io; |