编写一个猜数游戏
# 处理一次猜测
猜数游戏的第一部分会请求用户进行输入,并检查该输入是否满足预期的格式
use std::io;
fn main() {
println!("猜一个数字");
println!("请输入你猜测的数字");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("阅读行失败");
println!("你猜测的数字是:{}",guess);
}
use std::io;
把标准库(也就是所谓的 std)中的 io 模块引入当前的作用域中
# 使用变量来存储值
在 Rust 中,变量都是默认不可变的
let foo = 5; // foo是不可变的
let mut bar = 5; // bar是可变的
# 注意
1.函数String::new
后返回的结果:一个新的 String 实例。String 是标准库中的一个字符串类型,它在内部使用了 UTF-8 格式的编码并可以按照需求扩展自己的大小。
2.我们在程序的第一行使用了语句 use std::io。现在,我们将调用 io 模块中的关联函数 stdin:
use std::io;
io::stdin().read_line(&mut guess).expect("阅读行失败");
假如你没有在程序的开始处添加 use std::io;行那么就需要将这个函数调用修改为 std::io::stdin。
std::io::stdin().read_line(&mut guess).expect("阅读行失败");
3.Result 类型来处理可能失败的情况
.expect("阅读行失败");
# 运行
> cargo run
猜一个数字
请输入你猜测的数字
11
你猜测的数字是:11
# 生成一个随机数字
为了保证一定的可玩性,并使每局游戏都有不同的体验,这个生成的保密数字将会是随机的。为了让游戏不会过分困难,我们可以把这个随机数字限制在 1 到 100 之间。Rust 团队并没有把类似的随机数字生成功能内置到标准库中,而是选择将它作为 rand 包(rand crate)提供给用户。
Cargo.toml
[dependencies]
rand = "0.3.14"
运行 cargo build 时,Cargo 就会自动更新注册表中所有可用包的最新版本信息
use std::io;
use rand::Rng;
fn main() {
println!("猜一个数字");
let secret_number = rand::thread_rng().gen_range(1, 10);
println!("请输入你猜测的数字");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("阅读行失败");
println!("你猜测的数字是:{}", guess);
println!("随机数字是:{}", secret_number);
}
use rand::Rng
。这里的Rng
是一个 trait(特征),它定义了随机数生成器需要实现的方法集合。为了使用这些方法,我们需要显式地将它引入当前的作用域中。
第一行中的函数rand::thread_rng
会返回一个特定的随机数生成器:它位于本地线程空间,并通过操作系统获得随机数种子。随后,我们调用了这个随机数生成器的方法gen_range
。这个方法是在刚刚引入作用域的Rng rait
中定义的,它接收两个数字作为参数,并生成一个范围在两者之间的随机数。
# 运行
> cargo run
猜一个数字
请输入你猜测的数字
8
你猜测的数字是:8
随机数字是:2
# 比较猜测数字与保密数字
随机生成的保密数字,还有一个用户输入的猜测数字。接下来,在代码比较这两个数字
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("猜一个数字");
let secret_number = rand::thread_rng().gen_range(1, 10);
println!("请输入你猜测的数字");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("阅读行失败");
// 将String类型转换为数值类型
let guess: u32 = guess.trim().parse().expect("请输入一个数字");
println!("随机的数字是:{}", secret_number);
println!("你猜测的数字是:{}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("数字太小了"),
Ordering::Greater => println!("数字太大了"),
Ordering::Equal => println!("你赢了"),
}
}
1.Rust 允许使用同名的新变量 guess 来隐藏(shadow)旧变量的值。
# 运行
> cargo run
猜一个数字
请输入你猜测的数字
4
随机的数字是:6
你猜测的数字是:4
数字太小了
# 使用循环来实现多次猜测
在 Rust 中,loop 关键字会创建一个无限循环。我们可以将它加入当前的程序中,进而允许玩家反复地进行猜测抉择:
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("猜一个数字");
let secret_number = rand::thread_rng().gen_range(1, 10);
loop {
println!("请输入你猜测的数字");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("阅读行失败");
// 将String类型转换为数值类型
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("你猜测的数字是:{}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("数字太小了"),
Ordering::Greater => println!("数字太大了"),
Ordering::Equal => {
println!("你赢了");
break;
}
}
}
}
假如parse没能将字符串解析为数字,那么它将返回一个包含了具体错误信息的Err值。这个值会因为无法匹配Ok(num)
模式而跳过match
表达式的第一个分支,并匹配上第二个分支中的Err(_)
模式。这里的下画线_是一个通配符,它可以在本例中匹配所有可能的Err值,而不管其中究竟有何种错误信息。因此,程序会继续执行第二个分支中的代码:continue
,这条语句会使程序直接跳转至下一次循环,并再次请求玩家猜测保密数字。这样,程序便忽略了parse
可能会触发的那些错误。