学习 Rust 的第三天:如何编写一个猜数字的游戏

欢迎来到学习 Rust 的第三天!我参考的是 Steve Klabnik 的《Rust 编程语言》一书。今天我们要用 Rust 来制作一个猜数字的游戏。

引言

我们将创建一个游戏,它会在 1 到 100 之间随机选择一个数字,用户需要猜出这个数字,猜对了就赢了。

算法

伪代码如下所示:

rust 复制代码
secret_number = (生成一个随机数)  
loop {  
	写入"请输入你的猜测"  
	读取猜测  
	写入"你的猜测:${guess}"  
	  
	if(猜测 > secret_number){  
		写入"太高了!"  
	}else if(猜测 < secret_number){  
		写入"太低了!"  
	}else if(猜测==数字){  
		写入"你赢了!"  
		跳出循环  
	}  
}

步骤一:设置新项目

使用 cargo new 命令来设置一个项目:

bash 复制代码
$ cargo new guessing_game  
$ cd guessing_game

步骤二:声明变量并读取用户输入

文件名:main.rs

rust 复制代码
use std::io;  
  
fn main() {  
    println!("猜数字游戏");  
    println!("请输入你的猜测");  
  
    let mut guess = String::new();  
  
    io::stdin().read_line(&mut guess).expect("读取失败");  
    println!("你的猜测:{}", guess);  
}

让我们逐行解析这段代码:

  • use std::io; 这一行将 std::io(标准输入/输出)库引入到作用域中。std::io 库提供了许多处理输入/输出的有用功能。
  • fn main(){...} 这是主函数,程序的执行从这里开始。
  • println!("猜数字游戏"); println! 是一个宏,用于将文本打印到控制台。
  • println!("请输入你的猜测"); 这一行提示用户输入他们的猜测。
  • let mut guess = String::new(); 这一行声明了一个可变变量 guess 并将其初始化为空字符串。mut 表示该变量是可变的,即其值可以改变。String::new() 创建了一个新的空字符串。
  • io::stdin().read_line(&mut guess).expect("读取失败"); 这一行从标准输入(键盘)读取用户输入。输入的内容被放入 guess 字符串中。如果这个过程失败,程序将停止执行并显示"读取失败"的消息。
  • println!("你的猜测:{guess}"); 这一行打印出字符串"你的猜测:",后跟用户输入的内容。

步骤三:生成一个随机数

为了增加可玩性,每次游戏都应该产生不同的数字。Rust 标准库没有包含随机数功能,但 Rust 团队提供了一个 rand crate 来实现这个目的。

一个 Rust crate 就像是一个整齐打包的代码盒子,你可以轻松地在 Rust 编程语言中使用和与他人分享。

使用 rand crate:

文件名:Cargo.toml

toml 复制代码
[package]  
name = "guessing_game"  
version = "0.1.0"  
edition = "2021"  
  
[dependencies]  
rand = "0.8.5"  #添加这一行

理解 Cargo.toml 文件:

  1. [package]
  • name = "guessing_game":指定 Rust 包(或 crate)的名称为 "guessing_game"。
  • version = "0.1.0":指定 crate 的版本为 "0.1.0"。
  • edition = "2021":指定要使用的 Rust 版本(在本例中是 2021 版)。
  1. [dependencies]
  • rand = "0.8.5":添加对 "rand" crate 版本 "0.8.5" 的依赖。这意味着 "guessing_game" crate 依赖于 "rand" crate 提供的功能,具体版本为 0.8.5。

简单来说,这个配置文件(Cargo.toml)就像是你 Rust 项目的配方,指定了它的名称、版本、Rust 版本以及任何外部依赖项(在本例中是 "rand" crate)。

在这之后,不改变任何代码,运行 cargo build,为什么我们要这么做呢?

  • 获取依赖项: cargo build 获取并更新项目中在 Cargo.toml 中指定的依赖项。
  • 解决依赖关系: 它解决并确保安装了正确版本的依赖项。
  • 构建过程: 将 Rust 代码和依赖项编译成可执行文件或库。
  • 检查错误: 识别和报告编译错误,确保代码一致性。
  • 更新锁文件: 更新 Cargo.lock 文件以记录确切的依赖版本,以保证可重现性。

现在让我们看看代码:

rust 复制代码
use std::io;  
use rand::Rng;  
  
fn main() {  
    println!("猜数字游戏!");  
  
    let secret_number = rand::thread_rng().gen_range(1..=100);  
    println!("秘密数字:{}", secret_number);  
  
    println!("请输入你的猜测");  
  
    let mut guess = String::new();  
  
    io::stdin().read_line(&mut guess).expect("读取失败");  
    println!("你的猜测:{}", guess);  
}

运行程序:

rust 复制代码
$ cargo run

  
猜数字游戏!  
秘密数字:69  
请输入你的猜测  
32  
你的猜测:32

让我们来看看这里做了什么:

  • use rand::Rng;:这一行是一个导入语句,将 Rng trait 引入到作用域中,允许你使用它的方法。
  • rand::thread_rng():这部分初始化一个特定于当前线程的随机数生成器。rand::thread_rng() 函数返回一个实现了 Rng trait 的类型。
  • .gen_range(1..=100):利用随机数生成器(Rng trait),此代码调用 gen_range 方法生成一个指定范围内的随机数。范围被定义为 1..=100,意味着生成的数字应该在 1 到 100 之间(包括边界)。

第四步:将猜测与用户输入进行比较

现在我们已经获取了用户输入,程序将用户的猜测与秘密数字进行比较,以确定他们是否猜对了。如果猜测与秘密数字相匹配,用户就成功了;否则,程序将评估猜测是太大还是太小。

让我们看看代码,然后分解一下:

rust 复制代码
use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("猜数字游戏开始!");

    let secret_number = rand::thread_rng().gen_range(1..=100);
    println!("秘密数字:{}", secret_number);

    println!("请猜测一个数字");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess).expect("读取输入失败");

    let guess: u32 = guess.trim().parse().expect("请输入一个数字!");
    println!("你的猜测:{}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("太小了!"),
        Ordering::Greater => println!("太大了!"),
        Ordering::Equal => println!("你赢了!"),
    }
}

运行程序:

rust 复制代码
$ cargo run
猜数字游戏开始!
秘密数字 : 48
请猜测一个数字
98
你的猜测:98
太大了!

解释:

  • let guess: u32 = guess.trim().parse().expect("请输入一个数字!");:重新定义了变量 guess,将字符串解析为无符号32位整数。如果解析失败,则打印错误消息。
  • match guess.cmp(&secret_number) { ... }:使用 match 语句将用户的猜测与秘密数字进行比较,处理三种情况:小于、大于或等于秘密数字。

变量重新定义:

  • 变量重新定义是指声明了与现有变量相同名称的新变量,从而有效地"遮蔽"了先前的变量。
  • 在这段代码中,let guess: u32 = ... 就是一个变量重新定义的例子。第二个 guess 遮蔽了第一个,将其类型从 String 更改为 u32。变量重新定义通常用于重新分配具有新值和类型的变量,同时保持相同的名称。

第五步:循环直到用户赢得游戏

在第五步中,程序实现了一个循环结构,重复提示用户猜测,直到他们正确猜出秘密数字,就像我们在算法中看到的那样。

和往常一样,先看代码,再解释:

rust 复制代码
use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("猜数字游戏开始!");

    let secret_number = rand::thread_rng().gen_range(1..=100);
    println!("秘密数字:{}", secret_number);

    loop {
        println!("请猜测一个数字");

        let mut guess = String::new();

        io::stdin().read_line(&mut guess).expect("读取输入失败");

        let guess: u32 = guess.trim().parse().expect("请输入一个数字!");
        println!("你的猜测:{}", guess);

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

运行程序:

bash 复制代码
$ cargo run
猜数字游戏开始!
秘密数字:23
请猜测一个数字
4
你的猜测:4
太小了!
请猜测一个数字
76
你的猜测:76
太大了!
请猜测一个数字
23
你的猜测:4
你赢了!

解释:

  • loop{...} 语句用于创建一个无限循环。
  • 我们使用 break 语句来退出程序,当变量 guesssecret_number 相同时。

第六步:异常处理

在第六步中,我们要处理无效输入和由此引起的错误。例如:在提示中输入一个字符串

rust 复制代码
use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("猜数字游戏开始!");

    let secret_number = rand::thread_rng().gen_range(1..=100);
    println!("秘密数字:{}", secret_number);

    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(_) => continue,
        };
        println!("你的猜测:{}", guess);

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

运行程序:

bash 复制代码
$ cargo run
猜数字游戏开始!
秘密数字:98
请猜测一个数字
喵喵
请猜测一个数字
43
你的猜测:43
太小了!

解析用户输入:

  • 尝试将 guess 中的字符串解析为无符号32位整数。
  • 使用 trim 方法从用户的输入中去除前导和尾随空白。

匹配语句:

  • match 语句检查解析操作的结果。
  • Ok(num) => num:如果解析成功,则将解析的数字赋给变量 guess

错误处理:

  • Err(_) => continue:如果解析过程中出现错误,占位符 _ 匹配任何错误,循环内的代码继续执行,提示用户再次输入。

总结

在本文中,我们通过构建一个猜数字游戏开始了我们的 Rust 第三天学习之旅。以下是涉及的关键步骤和概念的总结:

简介

  • 目标是创建一个游戏,让用户猜一个在1到100之间随机生成的数字。

算法

  • 概述了一个通用的算法,提供了游戏逻辑的高层概览。

第一步:设置一个新项目

  • 使用 cargo new 创建了一个名为 "guessing_game" 的新 Rust 项目。

第二步:声明变量和读取用户输入

  • 使用 std::io 介绍了基本的输入/输出功能。
  • 展示了如何读取用户输入,初始化变量和打印消息。

第三步:生成随机数

  • 添加了 rand crate 作为依赖项来生成随机数。
  • 使用 rand::thread_rng().gen_range(1..=100) 生成了一个介于1和100之间的随机数。

第四步:将猜测与用户输入进行比较

  • 引入了变量重定义,并将用户输入与秘密数字进行比较。
  • 使用 match 语句处理不同的比较结果。

第五步:循环直到用户赢得游戏

  • 实现了循环结构,允许用户重复猜测,直到他们正确猜出秘密数字为止。

第六步:异常处理

  • 通过为用户输入解析过程添加错误处理来解决潜在错误。
  • 使用 continue 语句在发生错误时跳过当前迭代,并再次提示用户。
相关推荐
blackA_3 小时前
数据库MySQL学习——day4(更多查询操作与更新数据)
数据库·学习·mysql
梁下轻语的秋缘5 小时前
每日c/c++题 备战蓝桥杯(P1049 [NOIP 2001 普及组] 装箱问题)
c语言·c++·学习·蓝桥杯
球求了6 小时前
C++:继承机制详解
开发语言·c++·学习
UestcXiye6 小时前
Rust 学习笔记:函数和控制流
rust
时光追逐者6 小时前
MongoDB从入门到实战之MongoDB快速入门(附带学习路线图)
数据库·学习·mongodb
一弓虽6 小时前
SpringBoot 学习
java·spring boot·后端·学习
genggeng不会代码8 小时前
用于协同显著目标检测的小组协作学习 2021 GCoNet(总结)
学习
搞机小能手8 小时前
六个能够白嫖学习资料的网站
笔记·学习·分类
264玫瑰资源库8 小时前
斗鱼娱乐电玩平台源码搭建实录
运维·服务器·游戏·娱乐
Source.Liu10 小时前
【mdlib】0 全面介绍 mdlib - Rust 实现的 Markdown 工具集
rust·markdown