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; |