说明
我们一起手动完成一个项目,加速上手rust的的速度。
他是这样工作的:
程序随机生成1-10内的随机数,然后我们用户输入一个数,来进行猜测,然后提示猜大了,还是猜小了,如果猜对了,则退出程序。
新建一个项目
cargo new guessing_game
处理用户输入
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!("You guessed: {}", guess);
}
引入io库
use std::io;
引入io
库,因为我们要接收用户输入
let mut guess = String::new();
这里我们创建一个变量。这里的let mut
是mutable 的缩写,代表这个变量是可被修改的,如果不加mut
代表是一个常量。
创建一个字符串
String::new()
用来创建一个字符串实例,是一个可增长的UTF8字符序列。
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
上面我们通过use
命令引入了io库
这里
io::stdin()
表示一个输入流
read_line
表示从键盘读入一行.
&mut guess
这里就比较有意思了, &
代表这是一个引用,那么mut
我们前面学过了表示是一个可变的。结合到一块就是可变的引用
异常处理
.expect("Failed to read line");
这里是一种异常处理手段
read_line() 返回值是 Result 类型
关于Rust类型rust中是这样定义的
enum Result<T, E> {
Ok(T),
Err(E),
}
相信有别的编程语言经验的人,一眼就能看出这是一个枚举类型。
Ok表示操作成功,Err表示出现错误.
那么这里的 expect
方法的作用是,如果Result的值为Err那么,输出错误信息Failed to read line
,如果是Ok,则不报错。
占位输出
println!("You guessed: {}", guess);
这里的{}
是占位符,表示把后面的guess
填充到前面{}
里
生成随机数
引入第三方crate
这里我们需要使用第三方craterand
在Cargo.toml中添加依赖.
[dependencise]
rand = "0.8.3"
cargo update
生成一个随机数
use std::io;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..101);
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);
}
use rand::Rng
Rng是一个trait
,定义了随机数生成器应实现的方法,想用这些方法的话,此trait必须在作用域中。
rand::thread_rng()
位于当前执行线程本地环境中,从操作系统获取seed,然后使用gen_range
方法来实现生成一个随机数。1..101
是一个范围表达式,表示从取值范围为100 以内
数字比较
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
// ---snip---
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
第一行代码是use
从标准库中引入了一个std::cmp::Ordering
类型,他和Result一样也是枚举类型.
pub enum Ordering {
Less,
Equal,
Greater,
}
三种值表示三种不同的比较结果
然后用match
表达式由分支构成,一个分支包含一个模式类似于其他语言的 switch case 语句
.
类型转换
// --snip--
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
上面我们接受了用户输入数据,我们需要对数据进行处理。
trim()
用来消除空格,parse()
用来进行类型转换。
let guess: u32 = guess.trim().parse().expect("Please type a number!");
这里我们创建了一个 guess变量,等等,上面不是也有个 guess 变量吗?这里用到了变量隐藏的概念. rust允许复用之前使用的变量名称,而不是创建两个不同的变量,意味着后面的变量会将前面的变量完全覆盖。let guess:u32
表示常量类型为 unsigned int32
无符号32位整型。parse方法调用容易产生错误,因此需要expect
来进行异常处理。
多次猜测
// --snip--
println!("The secret number is: {}", secret_number);
loop {
println!("Please input your guess.");
// --snip--
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
}
这里 loop 表示创建一个死循环,不断地执行循环体内容。但是这并不是我们想要的,我们游戏应当在用户猜测正确后自动退出,改进上面代码.
// --snip--
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
哈哈哈,这里的 break
和别的语言一样,表示跳出循环。
处理无效输入
如果我们使用 expect函数的话,会导致程序崩溃,进而导致程序终止。虽然从一定程度上解决了输错问题,但是降低了游戏的可玩性。
因此我们要在用户输错时进行,处理。
通过前面的学习我们知道 parse() 返回值是一个 Result
类型,那么我们可用 match来进行 匹配,我们只需要捕获 Err
。
// --snip--
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {}", guess);
// --snip--
然后调用 continue 语句,跳过本次循环,进而解决问题。
完整代码
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..101);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}