Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.4 猜数游戏Pt.4 循环询问

2.4.0. 本篇知识点

这是猜数游戏的最后一个部分,本期知识点为:

  • loop循环
  • break
  • continue
  • match的灵活使用
  • 枚举类型的处理方式

2.4.1. 游戏目标

  • 生成一个1到100间的随机数- 提示玩家输入一个猜测
  • 猜完之后,程序会提示猜测是太大了还是太小了
  • 循环询问,如果猜测正确,那么打印一个庆祝信息,程序退出(本篇会涉及)

2.4.2. 代码实现

Step 1:实现循环

之前代码中,我们实现了一次输入的比较,接下来我们要实现多次询问和多次比较,直到用户猜到正确的数。

以下是截止到本篇之前的代码:

use std::io;
use rand::Rng;
use std::cmp::Ordering;
fn main() {
    let range_number = rand::thread_rng().gen_range(1..101);

    println!("猜数游戏 Number Guessing Game");
    println!("猜一个数 Guess a number");

    let mut guess = String::new();
    io::stdin().read_line(&mut guess).expect("无法读取行 Could not read the line");

    let guess:u32 = guess.trim().parse().expect("请输入一个数字 Please enter a number");

    println!("你猜测的数是The number you guessed is:{}", guess);

    match guess.cmp(&range_number){
        Ordering::Less => println!("太小了 Too small"),
        Ordering::Greater => println!("太大了 Too big"),
        Ordering::Equal => println!("你赢了 You win"),
    }

    println!("神秘数字是 The secret number is: {}", range_number);
}

而我们需要重复执行的代码就是从询问到对比到输出比较结果的部分,具体到代码上就是:

#![allow(unused)]
fn main() {
println!("猜一个数 Guess a number");

    let mut guess = String::new();
    io::stdin().read_line(&mut guess).expect("无法读取行 Could not read the line");

    let guess:u32 = guess.trim().parse().expect("请输入一个数字 Please enter a number");

    println!("你猜测的数是The number you guessed is:{}", guess);

    match guess.cmp(&range_number){
        Ordering::Less => println!("太小了 Too small"),
        Ordering::Greater => println!("太大了 Too big"),
        Ordering::Equal => println!("你赢了 You win"),
    }
}

Rust提供了一个无限循环的关键字loop,其结构如下:

#![allow(unused)]
fn main() {
loop {
	//在这里写想要无限循环的代码
	//Write code here that wants to loop indefinitely
}
}

只需要把需要重复执行的代码放入这个结构中即可:

#![allow(unused)]
fn main() {
loop {
    println!("猜一个数 Guess a number");

    let mut guess = String::new();
    io::stdin().read_line(&mut guess).expect("无法读取行 Could not read the line");

    let guess:u32 = guess.trim().parse().expect("请输入一个数字 Please enter a number");

    println!("你猜测的数是The number you guessed is:{}", guess);

    match guess.cmp(&range_number){
        Ordering::Less => println!("太小了 Too small"),
        Ordering::Greater => println!("太大了 Too big"),
        Ordering::Equal => println!("你赢了 You win"),
    }
}
}

Step 2:退出程序的条件

但是需要注意的是,程序写到这里能实现循环询问,但是会一直询问下去不会退出,而按照逻辑,在用户猜对后打印提示信息后程序就应该停止询问了。这里就需要退出循环的关键字break,把它放在Ordering Equal这个分支(分支的概念在上一篇文章中就有解释,这里不再重复)后即可,记得一个分支下如果要执行多行代码要把代码块部分用{}框住

#![allow(unused)]
fn main() {
match guess.cmp(&range_number){
    Ordering::Less => println!("太小了 Too small"),
    Ordering::Greater => println!("太大了 Too big"),
    Ordering::Equal => {
        println!("你赢了 You win");
        break;
    }
}
}

Step 3:错误输入的处理

这个代码还有一个问题;如果用户的输入不是整数数字,那么程序在.parse()时就会返回Err,.expect()接收到后就会直接终止程序。而正确的逻辑是如果输入有误应打印错误信息,然后让用户再次输入。

这该怎么办呢?在*2.1 猜数游戏Pt.1 一次猜测* 中提到过.parse()的返回值是一个枚举类型,如果成功转换,返回值会是:Ok+转换好的内容;如果失败,返回值是:Err+失败的原因;那在哪里提到过这个枚举类型呢?没错,在上一篇文章中我们提到了Ordering这个枚举类型,在那篇文章中我们使用了match来处理大于小于和等于的情况。那么在这里我们也可以使用match来处理.parse()的返回值,对不同的情况执行不同的操作,具体就是:如果成功转换,继续执行;如果失败,跳过下面的代码执行下一次循环。Rust中跳过本次循环的关键字更其它语言一样都是continue

具体该怎么改代码呢?就是把let guess:u32 = guess.trim().parse().expect("请输入一个数字 Please enter a number");这一行改为:

#![allow(unused)]
fn main() {
let guess:u32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => continue,
};
}
  • Ok(num) => num:这个分支负责处理成功转换,返回值是Ok+转换好的内容的情况。Ok是这个枚举类型的一个变体,OK后的()里是这个枚举类型中附带的转换好的内容(u32),这里()里写了num意思就是把转换好的内容绑定到num上,这个num的值会被传递给match表达式的结果,最终赋值给guess

  • Err(_) => continue:这个分支用于处理转换失败,返回值是Err+失败的原因的情况。Err是枚举类型的一个变体,Err后的()里是这个枚举类型中附带的失败的原因(&str),()里是_表示不关心错误信息,只需要知道是Err即可。

这里使用match来代替.expect()处理错误,这是Rust中处理错误的管用手段。

2.4.3. 代码效果

一下是完整代码:

use std::io;
use rand::Rng;
use std::cmp::Ordering;
fn main() {
    let range_number = rand::thread_rng().gen_range(1..101);

    println!("猜数游戏 Number Guessing Game");
    loop {
        println!("猜一个数 Guess a number");

        let mut guess = String::new();
        io::stdin().read_line(&mut guess).expect("无法读取行 Could not read the line");

        let guess:u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("你猜测的数是The number you guessed is:{}", guess);

        match guess.cmp(&range_number){
            Ordering::Less => println!("太小了 Too small"),
            Ordering::Greater => println!("太大了 Too big"),
            Ordering::Equal => {
                println!("你赢了 You win");
                break;
            },
        }
    }

    println!("神秘数字是 The secret number is: {}", range_number);
}

效果: