diff --git a/projects/guessing_game/src/main.rs b/projects/guessing_game/src/main.rs index 8585db2..8e64426 100644 --- a/projects/guessing_game/src/main.rs +++ b/projects/guessing_game/src/main.rs @@ -7,23 +7,28 @@ fn main() { let secret_number = rand::thread_rng().gen_range(1..=100); - println! ("秘密数字为:{secret_number}"); + // println! ("秘密数字为:{secret_number}"); - println! ("请输入你的猜数。"); + loop { + println! ("请输入你的猜数。"); - let mut guess = String::new(); + let mut guess = String::new(); - io::stdin() - .read_line(&mut guess) - .expect("读取行失败/failed to read line"); + io::stdin() + .read_line(&mut guess) + .expect("读取行失败/failed to read line"); - let guess: u32 = guess.trim().parse().expect("请输入一个数字!"); + let guess: u32 = guess.trim().parse().expect("请输入一个数字!"); - println! ("你猜的是:{guess}"); + println! ("你猜的是:{guess}"); - match guess.cmp(&secret_number) { - Ordering::Less => println! ("太小!"), - Ordering::Greater => println! ("太大!"), - Ordering::Equal => println! ("你赢了!"), + match guess.cmp(&secret_number) { + Ordering::Less => println! ("太小!"), + Ordering::Greater => println! ("太大!"), + Ordering::Equal => { + println! ("你赢了!"); + break; + }, + } } } diff --git a/src/Ch02_Programming_a_Guessing_Game.md b/src/Ch02_Programming_a_Guessing_Game.md index 853a34d..c06973f 100644 --- a/src/Ch02_Programming_a_Guessing_Game.md +++ b/src/Ch02_Programming_a_Guessing_Game.md @@ -615,15 +615,19 @@ $ cargo run  101 ✘  太大了! ``` -很棒!尽管在猜数前加了一些空格,程序仍然算出了用户猜的是 `76`。多运行几次这个程序,来验证在各种输入时其不同的表现:猜对一个数、猜个太大的数,以及猜个过小的数。 -现在这个游戏大致在工作了,然而用户只能猜一次。下面就来通过添加循环对其进行修改! +不错!即使在猜数前添加了空格,程序仍然能判断出,用户猜测的数字是 76。请多运行几次程序,验证在不同输入情况下的不同行为:猜对数字、猜的数字太大、猜的数字太小等等。 -## 用循环来实现多次猜数 +我们现在已经让这个游戏的大部分工作了,但用户只能猜一次数。我们就来通过添加一个循环,改变这种情况! + + +## 通过循环实现多次猜数 **Allowing Multiple Guesses with Looping** -关键字 `loop` 创建出无限循环。这里就要添加一个循环,来让用户有更多机会去猜数: + +`loop` 关键字会创建出一个无限循环。我们将添加一个让用户有更多机会猜出数字的循环: + 文件名:`src/main.rs` @@ -638,17 +642,18 @@ $ cargo run  101 ✘  // --跳过-- match guess.cmp(&secret_number) { - Ordering::Less => println! ("太小了!"), - Ordering::Greater => println! ("太大了!"), + Ordering::Less => println! ("太小!"), + Ordering::Greater => println! ("太大!"), Ordering::Equal => { println! ("你赢了!"); break }, } } } ``` -可以看到,这里已将自猜数输入提示开始的全部代码,移入到循环中了。请确保循环中的那些代码行,都另外缩进四个空格,然后再次运行这个程序。现在程序将会一直要求另一猜数,这实际上引入了新的问题。好像是用户无法退出了! +正如咱们所看到的,我们把从猜测输入提示开始的所有内容,都移到了一个循环中。请务必将循环内的那些行,缩进另外四个空格,然后再次运行程序。这个程序现在将一直不停要求另一个猜数,这实际上引入了一个新问题。用户似乎无法退出! + +用户可以始终通过使用键盘快捷键 `ctrl-c` 来中断这个程序。但还有一种方法可以摆脱这个贪得无厌的怪物,正如 [“将猜测与秘密数字进行比较”](#将猜数与秘数相比较) 小节,`parse` 的讨论中所提到的:如果用户输入的答案不是数字,这个程序就会崩溃。我们可以利用这一点,允许用户退出,如下所示: -用户可一直通过键盘快捷键 `Ctrl-C`,来中断这个程序。不过还是有别的方法,来退出这头贪厌的怪兽,就像在 [将猜数与秘密数字比较](#将猜数与秘数相比较)中对 `parse` 方法讨论中提到的那样:在用户输入了非数字的答案时,程序就会崩溃。这里就利用了那个,来实现用户退出,如下所示: ```console $ cargo run @@ -682,11 +687,16 @@ thread 'main' panicked at '请输入一个数字!: ParseIntError { kind: Inval note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ``` -敲入 `quit` 就会退出这游戏,不过正如所注意到的,这样做将就要敲入别的非数字输入。至少可以是这种做法是次优的;这里想要在猜到了正确数字时,游戏也要停止。 +输入 `quit` 将退出这个游戏,但咱们会发现,输入任何其他非数字的输入,也会退出游戏。至少可以说,这是次优的;我们希望在猜中正确数字后,这个游戏也会停止。 + ### 猜对后的退出 -下面就来通过添加一条 `break` 语句,将游戏编程为在用户赢了时退出: +**Quitting After a Correct Guess** + + +我们来通过添加一个 `break` 语句,将这个游戏编程为在用户获胜后退出: + 文件名:`src/main.rs` @@ -694,22 +704,27 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace // --跳过-- match guess.cmp(&secret_number) { - Ordering::Less => println! ("太小了!"), - Ordering::Greater => println! ("太大了!"), + Ordering::Less => println! ("太小!"), + Ordering::Greater => println! ("太大!"), Ordering::Equal => { println! ("你赢了!"); - break + break; }, } } } ``` -在 `你赢了!` 之后添加上 `break` 代码行,就令到游戏在用户猜中了秘密数字时,退出那个循环。由于该循环是 `main` 函数体的最后部分,因此退出循环也意味着退出这个程序。 +在 `你赢了!` 后面添加 `break` 这行,令到程序在用户猜对秘密数字时,退出那个循环。退出那个循环,也意味着退出这个程序,因为循环是 `main` 的最后一部分。 + +> **译注**:这里有个有趣的地方,`break` 后的分号可有可无,`match` 表达式最后支臂后的逗号,也是可有可无的。 ### 无效输入的处理 +**Handling Invalid Input** + + 为了进一步改进游戏表现,而不要在用户输入了非数字时将程序崩溃掉,那么接下来就要使得游戏忽略非数字,从而用户可以继续猜数。通过把`guess`从 `String` 转换为 `u32` 的那行加以修改,来完成这个目的,如下面的清单 2-5 所示: 文件名:`src/main.rs`