学习 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 语句在发生错误时跳过当前迭代,并再次提示用户。
相关推荐
HackKong7 分钟前
小白怎样入门网络安全?
网络·学习·安全·web安全·网络安全·黑客
螺旋天光极锐斩空闪壹式!23 分钟前
自制游戏:监狱逃亡
c++·游戏
Bald Baby28 分钟前
JWT的使用
java·笔记·学习·servlet
天晟科技33 分钟前
GameFi的前景:游戏与金融的未来交汇点
游戏·金融·区块链
心怀梦想的咸鱼1 小时前
UE5 第一人称射击项目学习(四)
学习·ue5
AI完全体1 小时前
【AI日记】24.11.22 学习谷歌数据分析初级课程-第2/3课
学习·数据分析
Mephisto.java2 小时前
【大数据学习 | Spark-Core】Spark提交及运行流程
大数据·学习·spark
PandaCave2 小时前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
yuwinter2 小时前
鸿蒙HarmonyOS学习笔记(2)
笔记·学习·harmonyos
red_redemption3 小时前
自由学习记录(23)
学习·unity·lua·ab包