学习 Rust

学习 Rust

首页休闲益智你描述我猜更新时间:2024-05-11
游戏说明及要求:

“猜数字”是一个简单的猜数字游戏,游戏生成一个secret number,然后用户尝试猜数字GUESS。

游戏应该告诉用户他/她的猜测是否是too big或too small,如果他/她猜到了秘密数字,则用户获胜并且游戏退出。

应检查用户输入是否存在无效的非数字值并警告用户。

应跟踪用户尝试的次数并在用户获胜时显示。
用户可以通过键入 退出游戏quit。
游戏应该持续运行,直到用户获胜或退出。

创建一个新项目:

就像我们之前学过的那样,输入以下命令来创建一个新guess_the_number项目并将目录 (cd) 更改到其中:

cargo new guess_the_number cd guess_the_number

我希望您查看一下cargo.toml通过新项目为您生成的文件:

# cargo.toml [package] name = "guess_the_number" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies]

现在,我们假设它cargo.toml是描述您的应用程序并列出其依赖项的地方(目前没有)。可以将其想象为 Python 的requirements.txt使用方式pip,pip install -r requiremts.txt但具有更多细节。

和往常一样,cargo它生成了一个“Hello, World”主函数供我们开始。

处理用户输入:

我们的首要任务是获取用户从标准输入输入的内容。输入以下内容:

use std::io; fn main() { println!("Welcome to: GUESS THE NUMBER game!"); println!("Please input your guess ..."); let mut guess = String::new(); io::stdin() .read_line(&mut guess) .expect("Unable to parse input!"); println!("Your guess is {guess}") }

这是本系列中我们第一次从应用程序“导入库”。我们用use关键字来做到这一点。这里我们io从名为 的标准库导入了该库std。

use std::io;

接下来,我们需要一些地方来保存用户的猜测。因此,我们定义了guess变量:

let mut guess = String::new();

这个mut关键词并不新鲜,但却是String::new()。因为我们不知道用户会写什么,所以guess不能是字符串文字(记住?字符串文字在代码中硬编码并在编译时已知)。相反,我们使用String类型及其关联new函数,该函数在内存中创建一个大小未知的“空”位置。

⚠️下一篇文章将详细介绍 String 类型

之后,我们使用io导入的库并调用其stdin()函数,该函数返回 StdIn 的“句柄”,该句柄具有read_line读取 StdIn 的函数。有趣的是我们传递给read_line函数的内容,我们将“可变引用”传递给变量guess,以便函数可以更改其值(可变),但不获取它的所有权(引用)。

新 Rust 术语:“所有权”基本上是保证 Rust 内存安全的原因。我将在下一篇文章中详细讨论它,但现在知道默认情况下变量不能在 Rust 中来回切换作用域。这是“main”作用域和“read_line”函数作用域。

这段代码中的最后一个新部分是.expect("Unable to parse input!"). 这是因为它read_line不返回值,而是返回一个Result枚举(枚举),在本例中它有两个变体(将变体视为值),Ok并且Err. 默认情况下,如果Resultis Ok,则返回用户输入的值。否则,如果Result是Err,程序“恐慌”,显示定义的错误消息。

继续输入cargo run并验证您输入的内容是否已打印出来。

使用外部crate:

现在我们需要一个“随机”的秘密数字供用户猜测。这次我们将需要一个名为 的外部“板条箱” rand。

新的 Rust 术语:“ crate ”是 Python 的包,相当于 Rust。访问crates.io查看 Rust 的所有可用 crate。

要在您的应用程序中使用 crate,您可以将它们列在cargo.toml我们之前讨论的文件中。

[package] name = "guess_the_number" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] rand = "0.8.5"

我们将在后面的文章中更详细地讨论 Rust 依赖关系。现在,请知道您用来cargo.toml列出依赖项。现在运行您的应用程序,这次您将看到正在下载并安装以下“crate”:


cargo了解您的依赖项cargo.toml,如果您再次运行您的应用程序,您会发现没有再次下载或安装任何内容。另一件值得注意的事情是cargo.lock驻留在项目根目录中的文件。cargo创建此文件以“锁定”依赖项版本以确保一致的构建。如果cargo找到cargo.lock,则将其用于依赖项列表而不是cargo.toml.

生成一个秘密号码:

现在我们使用rand我们列出的板条箱,cargo.toml就像我们之前对io库所做的那样。我们的代码变成这样:

use rand::Rng; use std::io; fn main() { println!("Welcome to: GUESS THE NUMBER game!"); // Generate secret number let secret_number = rand::thread_rng().gen_range(1..=100); println!("The secret number is {secret_number}"); println!("Please input your guess ..."); let mut guess = String::new(); io::stdin() .read_line(&mut guess) .expect("Unable to parse input!"); println!("Your guess is {guess}"); }

crate及其组件如何工作的细节rand现在并不重要。您需要知道的是,它用于生成 1 到 100(含)之间的随机数,如范围表达式高端之前的“=”所示1..=100。

运行代码,您每次都会看到不同的秘密数字。

猜测与密数比较:

我们有用户的guess和secret_number现在的,我们需要对它们进行比较。为了做到这一点,我们将导入另一个标准库组件,称为Orderingwithvariants enum,Less并且Greater我们Equal将使用关键字了解 Rust 的“模式匹配” match。
但在 Rust 中为了比较变量,它们必须是相同的类型。到目前为止,在我们的代码中,guessis 是String类型并且secret_number是i32隐式的(默认整数类型),因此两者之间的任何比较都会导致应用程序崩溃。为了解决这个问题,我们必须对解析变量的方式进行一些更改guess:

use rand::Rng; use std::cmp::Ordering; use std::io; fn main() { println!("Welcome to: GUESS THE NUMBER game!"); // Generate secret number let secret_number = rand::thread_rng().gen_range(1..=100); println!("The secret number is {secret_number}"); println!("Please input your guess ..."); let mut guess = String::new(); io::stdin() .read_line(&mut guess) .expect("Unable to parse input!"); // Shadowing let guess: u32 = guess .trim() .parse() .expect("Please input a number between 1 and 100"); println!("Your guess is {guess}"); // Pattern matching match guess.cmp(&secret_number) { Ordering::Less => println!("Too small"), Ordering::Greater => println!("Too big"), Ordering::Equal => println!("You win!"), } }

你会注意到我们再次重新定义了guess。但这一次是作为一种u32类型。这在 Rust 中被称为“遮蔽”。

新的 Rust 术语:“遮蔽”是指在 Rust 中的同一作用域内重新定义变量。您可以使用遮蔽来更改变量的类型和可变性。旧的价值观被思想摧毁了!

现在猜测是 u32,而 Secret_number 也被 Rust 编译器推断为 u32,我们可以比较它们(这在 Rust 中是允许的)。 我们使用 match 关键字进行模式匹配,因为我们将对 Secret_number 的引用(还记得 read_line 函数和所有权吗?)传递给 u32 类型上的 cmp 函数关联,然后其余部分是不言自明的 。运行代码并尝试获取 匹配块中的三个打印。

连续运行游戏:

您可能已经注意到,应用程序会在输入任何值时退出,并且如果用户给出了错误的猜测,游戏也不会继续请求用户猜测。 为了解决这个问题,我们将使用循环,如果用户猜对了,则“打破”循环。 此外,现在是跟踪用户尝试次数的好时机:

use rand::Rng; use std::cmp::Ordering; use std::io; fn main() { println!("Welcome to: GUESS THE NUMBER game!"); // Generate secret number let secret_number = rand::thread_rng().gen_range(1..=100); println!("The secret number is {secret_number}"); let mut tries = 0; loop { println!("Please input your guess ..."); let mut guess = String::new(); tries = 1; io::stdin() .read_line(&mut guess) .expect("Unable to parse input!"); // Shadowing let guess: u32 = guess .trim() .parse() .expect("Please input a number between 1 and 100"); println!("Your guess is {guess}"); // Pattern matching match guess.cmp(&secret_number) { Ordering::Less => println!("Too small"), Ordering::Greater => println!("Too big"), Ordering::Equal => { println!("You win! Took you {tries} tries to guess the secret number!"); break; } } } }

现在运行代码并验证如果用户猜对并显示尝试次数,应用程序是否退出。

处理无效输入并正确退出游戏:

游戏现已基本完成。我们只需要做一些用户体验增强,例如处理无效的用户输入(非数字),并在用户输入 时引入退出机制quit。

use rand::Rng; use std::cmp::Ordering; use std::io; fn main() { println!("Welcome to: GUESS THE NUMBER game!"); // Generate secret number let secret_number = rand::thread_rng().gen_range(1..=100); println!("The secret number is {secret_number}"); let mut tries = 0; loop { println!("Please input your guess ..."); let mut guess = String::new(); tries = 1; io::stdin() .read_line(&mut guess) .expect("Unable to parse input!"); // If the user input "quit", the game quits. if guess.trim().to_lowercase() == "quit" { break; } // Shadowing let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => { println!("Please input a number between 1 and 100"); continue; } }; println!("Your guess is {guess}"); // Pattern matching match guess.cmp(&secret_number) { Ordering::Less => println!("Too small"), Ordering::Greater => println!("Too big"), Ordering::Equal => { println!("You win! Took you {tries} tries to guess the secret number!"); break; } } } }

对于这一部分,我们在读取标准输入行后quit使用简单的方法if进行检查。guess我们还修改了阴影部分guess以使用模式匹配。如果Result枚举返回Ok,则返回数字。否则,如果返回,Err则打印警告消息并继续循环。

完成并发布游戏:

最后,我们将删除打印秘密数字的行,因此完整的代码将是:

use rand::Rng; use std::cmp::Ordering; use std::io; fn main() { println!("Welcome to: GUESS THE NUMBER game!"); // Generate secret number let secret_number = rand::thread_rng().gen_range(1..=100); let mut tries = 0; loop { println!("Please input your guess ..."); let mut guess = String::new(); tries = 1; io::stdin() .read_line(&mut guess) .expect("Unable to parse input!"); // If the user input "quit", the game quits. if guess.trim().to_lowercase() == "quit" { break; } // Shadowing let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => { println!("Please input a number between 1 and 100"); continue; } }; println!("Your guess is {guess}"); // Pattern matching match guess.cmp(&secret_number) { Ordering::Less => println!("Too small"), Ordering::Greater => println!("Too big"), Ordering::Equal => { println!("You win! Took you {tries} tries to guess the secret number!"); break; } } } }

到目前为止,我们都是通过打字cargo build来构建我们的应用程序的。这会相对快速地构建应用程序,但放弃了一些可以使二进制文件运行得更快的优化。我们可以在 中看到生成的二进制文件target/debug。

对于发布版本,我们应该输入cargo build --release. 您会注意到构建阶段比平时花费的时间更长(我们讨论过的优化),现在我们将在target/release.

⚠️ 该--release标志也适用于cargo run

继续并输入以下内容:

cargo build --release

然后直接运行游戏就不用了cargo,如下:

# Linux ./target/release/guess_the_number

并且游戏会顺利运行。尝试从第一次尝试就猜出数字!

下一篇文章,我将开始讨论 Rust 的具体功能和特性,即“所有权”。到时候见

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved