游戏说明
游戏运行逻辑如下:
游戏效果展示
游戏代码
游戏完整代码如下:
use rand::Rng;
use std::io;
use std::cmp::Ordering;
fn main() {
println!("欢迎来到猜数游戏!");
//1、生成神秘数字
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("神秘数字已经生成!");
loop {
//2、让用户进行猜测
println!("请猜测:>");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取行");
//3、将用户输入的数字字符串转化为整型
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("请您输入一个合法的整数!");
continue;
}
};
//4、将用户猜测的数与神秘数字进行比较
match guess.cmp(&secret_number) {
Ordering::Less => println!("您猜测的数字太小了"),
Ordering::Greater => println!("您猜测的数字太大了"),
Ordering::Equal => {
println!("恭喜您猜对了, 神秘数字就是{}!", secret_number);
break;
}
}
}
}
下面对猜数字游戏中所用到的Rust语法和包(crate)进行讲解。
rand包
在rand包中有一个名为thread_rng的方法,该方法会返回一个特定的随机数生成器,通过调用该随机数生成器的gen_range方法即可在指定范围内生成随机数。如下:
use rand::Rng;
fn main() {
let secret_number = rand::thread_rng().gen_range(1, 101); //在[1,101)范围生成随机数
println!("生成的神秘数字是: {}", secret_number);
}
说明一下:
{}
,从第二个参数开始,各参数依次替换格式化字符串中的占位符。添加依赖
Cargo最主要的功能就是帮助我们管理和使用第三方库,在使用rand包编写代码之前,需要在Cargo.toml文件的[dependencies]片段下将rand包声明为依赖,并指明它的版本号。如下:
[package]
name = "guessing_game"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.3.14"
执行cargo build命令后,Cargo就会从crates.io网站下载并编译指定的包,并基于这些依赖编译我们自己的项目。如下:
说明一下:
0.3.14
实际上是^0.3.14
的简写,它表示任何与0.3.14
版本公共API相兼容的版本,因此我们下载不一定是我们指定版本号的包。Cargo.lock
Cargo提供了一套机制来保证我们构建的结果是可重现的,任何人是任何时候编译我们的代码都会生成相同的产物,因为Cargo会一直使用某个特定版本的依赖,直到我们手动指定了其他版本。
如果你确实需要升级某个依赖包,那么可以使用cargo update
命令,该命令会强制Cargo忽略Cargo.lock文件,并重新计算出所有依赖包中符合Cargo.toml声明的最新版本。如下:
说明一下:
io模块
在io模块中有一个关联函数叫做stdin,该函数会创建一个Stdin的实例并返回,通过这个实例来调用read_line方法即可读取用户的一行输入。如下:
use std::io;
fn main() {
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取行");
println!("输入的内容: {}", guess);
}
说明一下:
小贴士:Rust中很多类型都有new方法,因为这是创建类型实例的惯用函数名称。
Result枚举
对于Result枚举来说,它有Ok和Err两个变体:
Result枚举的定义如下:
pub enum Result<T, E> {
/// Contains the success value
Ok(T),
/// Contains the error value
Err(E),
}
Result类型中定义了一系列方法,其中有一个方法叫做expect,该方法接收一个字符串:
expect方法的定义如下:
impl<T, E> Result<T, E> {
//...
pub fn expect(self, msg: &str) -> T
where
E: fmt::Debug,
{
match self {
Ok(t) => t,
Err(e) => unwrap_failed(msg, &e),
}
}
//...
}
说明一下:
解析用户输入
现在我们已经能够获得用户的输入了,但此时guess变量是String类型的,而我们生成的神秘数字是整型的,为了能够将它们进行比较,需要将guess变量转化为整型。如下:
use std::io;
fn main() {
println!("请输入一个整数:>");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取行");
let guess: u32 = guess.trim().parse().expect("请您输入一个合法的整数!");
println!("解析成功, 输入的数字是: {}", guess);
}
说明一下:
多次处理用户输入
为了猜中神秘数字,用户可能需要进行多次猜测,因此我们也需要多次对用户的输入进行解析,在Rust中使用loop关键字即可创建一个无限循环。如下:
use std::io;
fn main() {
loop {
println!("请输入一个整数:>");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取行");
let guess: u32 = match guess.trim().parse().expect("请您输入一个合法的整数!");
println!("解析成功, 输入的数字是: {}", guess);
}
}
match表达式
当用户输入非数字字符串时,parse方法就会解析失败,这时最好让用户重新猜测,而不是终止程序,鉴于parse方法的返回值类型是Result类型,因此可以用match表达式来代替expect函数,这也是在Rust中处理错误的一个惯用手段。
代码如下:
use std::io;
fn main() {
loop {
println!("请输入一个整数:>");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取行");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("请您输入一个合法的整数!");
continue;
}
};
println!("解析成功, 输入的数字是: {}", guess);
}
}
说明一下:
进行猜测比较
现在要做的就是将用户猜测的数字和神秘数字进行比较。
这里也可以使用match表达式来处理。如下:
use rand::Rng;
use std::io;
use std::cmp::Ordering;
fn main() {
println!("欢迎来到猜数游戏!");
//1、生成神秘数字
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("神秘数字已经生成!");
loop {
//2、让用户进行猜测
println!("请猜测:>");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取行");
//3、将用户输入的数字字符串转化为整型
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("请您输入一个合法的整数!");
continue;
}
};
//4、将用户猜测的数与神秘数字进行比较
match guess.cmp(&secret_number) {
Ordering::Less => println!("您猜测的数字太小了"),
Ordering::Greater => println!("您猜测的数字太大了"),
Ordering::Equal => {
println!("恭喜您猜对了, 神秘数字就是{}!", secret_number);
break;
}
}
}
}
说明一下:
此外,也可以使用if else语句对用户猜测的数字和神秘数字进行比较。如下:
use rand::Rng;
use std::io;
fn main() {
println!("欢迎来到猜数游戏!");
//1、生成神秘数字
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("神秘数字已经生成!");
loop {
//2、让用户进行猜测
println!("请猜测:>");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取行");
//3、将用户输入的数字字符串转化为整型
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("请您输入一个合法的整数!");
continue;
}
};
//4、将用户猜测的数与神秘数字进行比较
if guess < secret_number {
println!("您猜测的数字太小了");
} else if guess > secret_number {
println!("您猜测的数字太大了")
} else {
println!("恭喜您猜对了, 神秘数字就是{}!", secret_number);
break;
}
}
}
说明一下: