一. 创建一个新的项目

使用cargo创建一个项目:

cargo new guessing_game && cd guessing_game

进入项目中有下面这几个文件:

src
main.rs -> 源文件
.gitignore -> git忽略文件
Cargo.lock -> 包含有关您的依赖项的确切信息,它由 Cargo 维护,不需要手动编辑.
Cargo.toml -> 描述你的依赖,并由自己编写

Cargo.toml文件如下:

[package]
name = "guessing_game"
version = "0.1.0"
edition = "2021"

[dependencies]

接着看一下main.rs文件内容,Cargo创建项目时会生成一个Hello World的代码

fn main() {
println!("Hello, world!");
}

下面我们执行cargo run命令编译运行:

Compiling rust-example v0.1.0 (file:///TestProject/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.20s
Running `target/debug/guessing_game`
Hello, world!

二. 处理一次猜测

猜数字游戏的第一部分会请求用户输入,并检查输入是否满足预期的格式,下面我们开始编写代码:

use std::io;

fn main() {
println!("=== 猜数字游戏 ==="); // @1
println!("> 请输入一个数字: ");
let mut guess = String::new(); // @2
io::stdin().read_line(&mut guess).expect("读取失败"); // @3
println!("> 您猜的是: {}", guess)
}

这里稍微解释下,后面文章都会有详细介绍,写过其他语言这个一定还是比较熟悉的:

  • 在@1中println!是一个宏,作用是输出字符串;
  • 在@2这一行是定义一个变量,但是我们注意到前面有一个mut的关键字,这个是为了标识变量的可变性;
    这里在举个例子:​​let foo1 = 5​​和​​let mut foo2 = 5​​其中foo1是不可变的,foo2是可变的。
  • 在@3中是引入标准库获取控制台输入的数据。

其他的就不多介绍了,后续章节都有详细介绍!

最后我们cargo run编译执行下:

Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/guessing_game`
=== 猜数字游戏 ===
> 请输入一个数字:
78
> 您猜的是: 78

三. 生成一个保密数字

接着这里我们需要生成一个随机数字,作为保密数字,这里需要使用cargo的一个包:rand

[dependencies]
rand = "0.8.5"

添加完之后需要执行cargo build安装下包,如果有IDEA的话,点下图这个位置也可以。

接着看一下代码里面如何使用:

// 0.8.5版本的gen_range写法和原来不一样,需要注意下
let secret_number: i32 = rand::thread_rng().gen_range(1..101);
println!("加密数字在1-101之间产生: {}", secret_number);

四. 比较猜测数字与保密数字

接着我们需要比较用户猜的数字和随机生成的保密数字是否一致

use std::cmp::Ordering;
use std::io;
use rand::Rng;

fn main() {
let secret_number = rand::thread_rng().gen_range(1..101);
println!("加密数字在1-101之间产生: {}", secret_number);
println!("=== 猜数字游戏 ===");
println!("> 请输入一个数字: ");
let mut guess = String::new(); // @1
io::stdin().read_line(&mut guess).expect("读取失败");
let guess: i32 = guess.trim().parse().expect("请输出正确的格式"); // @2
match guess.cmp(&secret_number) { // @3
Ordering::Less => println!("> 您猜的是: {}, 太小", guess),
Ordering::Greater => println!("> 您猜的是: {}, 太大", guess),
Ordering::Equal => println!("> 您猜的是: {}, 正确", guess)
};
}

这里的@1我们是通过String字符串进行接收用户输入的,但是保密数字是i32类型的,如果cmp比较会报类型错误,所以@2进行类型转换,trim函数是将字符前后的空格去掉,最后在@3使用模式匹配进行比较。

Ordering是一个枚举:

#[derive(Clone, Copy, Eq, Debug, Hash)]
#[stable(feature = "rust1", since = "1.0.0")]
#[repr(i8)]
pub enum Ordering {
/// An ordering where a compared value is less than another.
#[stable(feature = "rust1", since = "1.0.0")]
Less = -1,
/// An ordering where a compared value is equal to another.
#[stable(feature = "rust1", since = "1.0.0")]
Equal = 0,
/// An ordering where a compared value is greater than another.
#[stable(feature = "rust1", since = "1.0.0")]
Greater = 1,
}

最后编译执行:

Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/guessing_game`
加密数字在1-101之间产生: 78
=== 猜数字游戏 ===
> 请输入一个数字:
12
> 您猜的是: 12, 太小

五. 使用循环来实现多次猜测

上面我们直接输入数字比较之后直接输出结果就结束了,下面我们使用loop关键字,实现可以多次猜测。

use std::cmp::Ordering;
use std::io;
use rand::Rng;

fn main() {
let secret_number = rand::thread_rng().gen_range(1..101);
println!("加密数字在1-101之间产生: {}", secret_number);
loop { // @1
println!("=== 猜数字游戏 ===");
println!("> 请输入一个数字: ");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("读取失败");
let guess: i32 = guess.trim().parse().expect("请输出正确的格式");
match guess.cmp(&secret_number) {
Ordering::Less => println!("> 您猜的是: {}, 太小", guess),
Ordering::Greater => println!("> 您猜的是: {}, 太大", guess),
Ordering::Equal => { // @2
println!("> 您猜的是: {}, 正确", guess);
break;
}
};
}
}

增加loop关键字,可以实现类似​​while(true){}​​功能,另切在@2中如果猜对了,可以使用break跳出循环。

六. 处理非法输入

当我们输入一些非法的字符,可能会导致系统奔溃,如下:

Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/guessing_game`
加密数字在1-101之间产生: 26
=== 猜数字游戏 ===
> 请输入一个数字:
ttt
thread 'main' panicked at '请输出正确的格式: ParseIntError { kind: InvalidDigit }', src/main.rs:13:47
stack backtrace:
......
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

下面我们再使用模式匹配的match处理下异常情况​​continue​​,不要让其程序崩溃。

use std::cmp::Ordering;
use std::io;
use rand::Rng;

fn main() {
let secret_number = rand::thread_rng().gen_range(1..101);
println!("加密数字在1-101之间产生: {}", secret_number);
loop {
println!("=== 猜数字游戏 ===");
println!("> 请输入一个数字: ");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("读取失败");
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue
};
match guess.cmp(&secret_number) {
Ordering::Less => println!("> 您猜的是: {}, 太小", guess),
Ordering::Greater => println!("> 您猜的是: {}, 太大", guess),
Ordering::Equal => {
println!("> 您猜的是: {}, 正确", guess);
break;
}
};
}
}

编译执行,看一下结果:

Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/guessing_game`
加密数字在1-101之间产生: 85
=== 猜数字游戏 ===
> 请输入一个数字:
12
> 您猜的是: 12, 太小
=== 猜数字游戏 ===
> 请输入一个数字:
ttt
=== 猜数字游戏 ===
> 请输入一个数字:
85
> 您猜的是: 85, 正确