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

devtools/2024/9/22 22:14:16/

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

引言

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

算法

伪代码如下所示:

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

步骤一:设置新项目

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

$ 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

[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;},}}
}

运行程序:

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

解释:

  • loop{...} 语句用于创建一个无限循环。
  • 我们使用 break 语句来退出程序,当变量 guess 和 secret_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;},}}
}

运行程序:

$ 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 语句在发生错误时跳过当前迭代,并再次提示用户。

http://www.ppmy.cn/devtools/5932.html

相关文章

JAVA程序设计-对象设计

无论是根据某马还是某谷的适配教程做项目时候,发现了大部分都是重复的crud,大部分只要做好笔记复习即可,但是却往往忘记了编码设计,所以这里开始复习编码设计,对象设计中,长期使用Mp的那一套导致就是Service Mapper,一套梭哈完了,这样很容易忘记基本功夫 POJO&#xff1a; 简单…

Python基于Django搜索的目标站点内容监测系统设计,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

<计算机网络自顶向下> TCP拥塞

目录 TCP拥塞控制机制 TCP拥塞感知 TCP速率控制方法 TCP拥塞控制和流量控制的联合动作 TCP拥塞控制策略 TCP吞吐量 TCP公平性 TCP拥塞控制机制 端到端的拥塞控制机制 路由器不向主机提供有关拥塞的反馈信息 路由器负担较轻 符合网络核心简单的TCP/IP架构原则 端系统根据自…

C语言表达式求值、隐式类型转换、算数转换

文章目录 前言一、表达式求值二、隐式类型转换小案例 三、算数转换总结 前言 C语言表达式求值、隐式类型转换、算数转换等介绍 一、表达式求值 表达式求值的顺序一部分是由操作符的优先级和结合性决定的。 表达式求值由操作符优先级决定 优先级讨论的是相邻操作符 #includ…

MYSQL之增删改查(下)

前言&#xff1a; 以下是MySQL最基本的增删改查语句&#xff0c;很多IT工作者都必须要会的命令&#xff0c;也 是IT行业面试最常考的知识点&#xff0c;由于是入门级基础命令&#xff0c;所有所有操作都建立在单表 上&#xff0c;未涉及多表操作。 4.3 高级查询 4.3.1 聚合函…

【C++类和对象】拷贝构造与赋值运算符重载

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

【项目实战】记录一次PG数据库迁移至GaussDB测试(下)

上一篇分享了安装、迁移&#xff0c;本篇将继续分享迁移前操作、 DRS迁移数据、迁移后一致性检查、问题总结及解决方法。 目录 四、迁移前操作 4.1 源端(PG) 4.2 目标端(GaussDB库) 五、DRS迁移数据 5.1 创建复制用户 5.2创建迁移任务。 六、迁移后一致性检查 6.1使用…

【c++】vector的使用

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;我们本篇来到一个新的容器&#xff0c;vector的讲解和使用 目录 1.vector简单介绍2.vector的使用2.1构造函数2.2遍历vector2.3对容量操作2.4vector的增删查改 1.v…