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

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

引言

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

算法

伪代码如下所示:

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

步骤一:设置新项目

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

$ cargo new guessing_game  
$ cd guessing_game

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

文件名:main.rs

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 文件以记录确切的依赖版本,以保证可重现性。

现在让我们看看代码:

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);  
}

运行程序:

$ 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 之间(包括边界)。

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

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

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

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!("你赢了!"),}
}

运行程序:

$ 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。变量重新定义通常用于重新分配具有新值和类型的变量,同时保持相同的名称。

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

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

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

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 相同时。

第六步:异常处理

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

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.mzph.cn/news/820362.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是UX设计?

在这个先进的世界中&#xff0c;大城市都被称之为科技之都&#xff0c;在那里&#xff0c;你会经常发现人们在谈论各种应用程序的设计。如果你对应用程序设计有浓烈的兴趣&#xff0c;那你应该去了解一下它的两个基本方面&#xff0c;也就是 UX 设计和 UI 设计。UX 设计旨在处理…

《中医病证分类与代码》-中医疾病分类数据库

《中医病症分类与代码》由国家中医药管理局2020年底修订&#xff0c;目的是为中医疾病及证候的分类提供统一的规范。规定2021年起&#xff0c;各中医机构的临床科室及基层中医药的医师都应按照最新修订的《中医病症分类与代码》规范来填报病案及病历。 中医病证分类与代码数据库…

探索C# 11与.NET 7:入门指南与跨平台开发

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

【代码随想录算法训练营第五十五天 | LeetCode392.判断子序列 、115.不同的子序列】

代码随想录算法训练营第五十五天 | LeetCode392.判断子序列 、115.不同的子序列 一、392.判断子序列 解题代码C&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {vector<vector<int>> dp(s.size() 1, vector<int>(t.size()…

正则表达式(Regular Expression)

正则表达式很重要&#xff0c;是一个合格攻城狮的必备利器&#xff0c;必须要学会&#xff01;&#xff01;&#xff01; &#xff08;参考视频&#xff09;10分钟快速掌握正则表达式&#xff08;奇乐编程学院&#xff09;https://www.bilibili.com/video/BV1da4y1p7iZ在线测试…

算法4:x的平方根

在不使用 sqrt(x)函数的情况下&#xff0c;得到x的平方根的整数部分 重点考察:二分法、牛顿迭代 暴力法&#xff1a; public class Test4 {public static void main(String[] args){int a 80;for (int i1;i*i<a;i){if(i*i8){System.out.println(i);}if(i*i<a &&am…

C++进阶学习:C++11特性

C11是C语言的一个重要版本&#xff0c;引入了许多新的特性和改进。接下来进行这些新特性的学习&#xff01; 1.nullptr的引入 在C语言中&#xff0c;NULL表示空地址。而C中NULL被定义为字面量0。 这里我们通过打印x的类型名&#xff0c;发现NULL的类型名是int&#xff0c;而对…

React构建组件的方式有哪些,有什么区别?

React构建组件的方式有哪些&#xff0c;有什么区别&#xff1f; 1. 函数组件1.1 特点 2. 类组件2.1特点 3. 高阶组件3.1特点 4. 自定义Hook4.1特点 React 组件是构成React应用的基本单元。在React中&#xff0c;有几种不同的方式可以创建组件&#xff0c;每种方式都有其特定的用…

算法与数据结构要点速学——时间复杂度(大 O)

时间复杂度 (大 O) 首先&#xff0c;我们来谈谈常用操作的时间复杂度&#xff0c;按数据结构/算法划分。然后&#xff0c;我们将讨论给定输入大小的合理复杂性。 数组&#xff08;动态数组/列表&#xff09; 规定 n arr.length, 在结尾添加或删除元素&#xff1a;O(1)从任意…

【C++】力扣OJ题:找出只出现一次的数字

Hello everybody!这是我第一次写关于OJ题目的博客&#xff0c;因为正好学到完了C的STL库&#xff0c;就顺手刷了一些OJ题。 我今天要介绍的题目虽然是力扣上的简单题&#xff0c;但思想很巧妙&#xff0c;我觉得有必要和大家分享一下&#xff01; 1.题目 2.代码 class Solut…

数据中心配电解决方案及项目案例

安科瑞电气股份有限公司 祁洁 15000363176 一、方案背景 为了确保数据中心供电的可靠性&#xff0c;通常会将数据中心的配电关键组件进行冗余设计&#xff0c;关键组件&#xff08;例如 UPS 单元、冷却系统和备用发电机&#xff09;被复制。同时将这些配电设备纳入到监控系…

视频批量高效剪辑,支持将视频文件转换为音频文件,轻松掌握视频格式

在数字化时代&#xff0c;视频内容日益丰富&#xff0c;管理和编辑这些视频变得愈发重要。然而&#xff0c;传统的视频剪辑软件往往操作复杂&#xff0c;难以满足高效批量处理的需求。现在&#xff0c;一款全新的视频批量剪辑神器应运而生&#xff0c;它支持将视频文件一键转换…

Day01-环境准备与镜像案例

Day01-环境准备与镜像案例 1. 容器架构1.1 Iaas Paas Saas (了解)1.2 什么是容器1.3 容器vs虚拟机1.4 Docker极速上手指南1&#xff09;配置docker源(用于安装docker)2&#xff09;docker下载镜像加速的配置3&#xff09;自动补全 1.5 Docker C/S架构1.6 Docker的镜像管理1&…

Java链式编程

一&#xff1a;链式编程 可以简化编程。代码简洁。 定义&#xff1a; 链式编程&#xff1a;顾名思义&#xff0c;链子嘛。它是一种编程范式&#xff0c;它允许将多个函数或操作连接在一起&#xff0c;形成一个链条&#xff0c;以执行复杂的操作。 优点&#xff1a; 编程性…

【Gradio】Could not create share link

【Gradio】Could not create share link 写在最前面在服务器端一直运行一个Python脚本解决&#xff1a;下载frpc_linux_amd64文件&#xff0c;并添加权限原理 完整过程&#xff1a;先找gradio库位置&#xff0c;然后发现缺失文件1. 打开终端2. 使用 find 命令查找 gradio 目录3…

vue学习日记22:非父子通信(拓展)-provideinject

一、概念 二、实践 代码 App <template><div class"app">我是APP组件<button click"change">修改数据</button><SonA></SonA><SonB></SonB></div> </template><script> import SonA …

Java SDK 使用示例

我们使用同样的 Maven 模板去创建 use-test-sdk 项目&#xff0c;不同的是&#xff0c;我们需要在 use-tset-sdk 下创建一个 lib 文件夹&#xff0c;用来存放 test-sdk.jar 文件。 我们将 test-sdk.jar 拖放到 use-test-sdk/lib 下&#xff0c;然后我们就可以编写代码了&#…

VRTK(Virtual Reality Toolkit)深入介绍

VRTK是一个为Unity引擎设计的开源虚拟现实&#xff08;VR&#xff09;开发框架&#xff0c;旨在简化和加速VR应用的开发过程。这个工具包包含了一系列的模块和预设&#xff0c;使得开发者可以快速集成标准的VR功能&#xff0c;如物体交互、环境导航、用户界面管理等。下面将对V…

每日OJ题_BFS解决最短路④_力扣675. 为高尔夫比赛砍树

目录 力扣675. 为高尔夫比赛砍树 解析代码 力扣675. 为高尔夫比赛砍树 675. 为高尔夫比赛砍树 难度 困难 你被请来给一个要举办高尔夫比赛的树林砍树。树林由一个 m x n 的矩阵表示&#xff0c; 在这个矩阵中&#xff1a; 0 表示障碍&#xff0c;无法触碰1 表示地面&…

知道XRD标准品PDF卡片号,如何直接导出标准物质数据,简单快速一分钟完成(附jade下载安装方法)

知道XRD标准品PDF卡片号&#xff0c;如何直接导出标准物质数据&#xff08;附jade下载安装方法&#xff09; 网上找到的方法都是先检索再导出&#xff0c;我的样品根本检索不到&#xff0c;但是根据参考文献知道了自己的pdf卡片号&#xff0c;可通过jade直接导出数据 1.请安装…