基础永远值得花费90%的精力去学习加强。认识实践的重要性。
Ax Rust Game
让我们一起完成一个动手项目,进入 Rust!实践出真知。您将了解let
, match
, 方法、相关函数、使用外部 crate 等等!
我们将实现一个经典的初学者编程问题:猜谜游戏。它是这样工作的:程序将生成一个介于 1 和 100 之间的随机整数。然后它会提示玩家输入猜测。输入猜测值后,程序将指示猜测值是过低还是过高。如果猜对了,游戏将打印祝贺信息并退出。
Bx Create Project
要建立一个新项目
$ cargo new guessing_game
$ cd guessing_game
测试一下输出默认的hello world
$ cargo run
Cx Programming
猜测处理机制
直接在默认生成的这个文件中编写
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
}
解析:
该io
库来自标准库,称为std
,所以需要引入。
fn
声明一个函数体,main
函数是程序的入口。
println!
是一个将字符串打印到屏幕的宏。
赋值到变量
我们将创建一个变量来存储用户输入
let mut guess = String::new();
使用let
语句来创建变量
let apples = 5;
这一行创建了一个名为 5 的新变量apples
并将其绑定到值 5。在 Rust 中,变量默认是不可变的,添加mut
使其可变。
let apples = 5; // immutable
let mut bananas = 5; // mutable
接收用户输入
use std::io;
,现在我们将从模块中调用该stdin
函数io
,这将允许我们处理用户输入。
io::stdin()
.read_line(&mut guess)
该行.read_line(&mut guess)
调用read_line
标准输入句柄上的方法来获取用户的输入。用户输入的任何内容放入标准输入并将其附加到字符串中(不覆盖其内容),所以我们因此将该字符串作为参数传递。字符串参数需要是可变的,以便该方法可以更改字符串的内容。
&
表示此参数是一个引用,它为您提供了一种方法,让您的代码的多个部分访问一个数据,而无需多次将该数据复制到内存中。引用是一个复杂的特性,Rust 的主要优势之一是使用引用是多么安全和容易。你不需要知道很多细节来完成这个程序。现在,您需要知道的是,与变量一样,引用在默认情况下是不可变的。因此,您需要编写&mut guess
而不是 &guess
使其可变。
Result的潜在故障
者是result的一个处理错误的机制。不要的话会报错(编译的时候,不影响运行)。
.expect("Failed to read line");
可以写成一行
io::stdin().read_line(&mut guess).expect("Failed to read line");
具体就不说了。
Println!使用占位符打印内容
println!("You guessed: {guess}");
您可以使用花括号打印多个值,第一组花括号保存格式字符串后列出的第一个值,第二组保存第二个值,依此类推
例
let x = 5;
let y = 10;
println!("x = {} and y = {}", x, y);
此代码将打印x = 5 and y = 10
.
测试处理机制
使用cargo run
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
1
You guessed: 1
至此,游戏的第一部分就完成了:我们从键盘获取输入,然后打印出来。
生成密码
接下来,我们需要生成一个用户将尝试猜测的秘密数字。密码每次都应该不同,这样游戏就可以玩不止一次。我们将使用 1 到 100 之间的随机数,这样游戏不会太难。Rust 的标准库中还没有包含随机数功能。然而,Rust 团队确实提供具有上述功能的rand
板条箱(crate)。
使用板条箱获得更多功能
请记住,一个 crate 是 Rust 源代码文件的集合。我们一直在构建的项目是一个二进制 crate,它是一个可执行文件。rand
crate 是一个库 crate,其中包含旨在用于其他程序且不能单独执行的代码。
Cargo 对外部 crate 的协调是 Cargo 真正发光的地方!
引入:我们需要修改Cargo.toml文件以包含rand
crate 作为依赖项。请务必rand
完全按照我们在此处指定的版本号使用此版本号,否则本教程中的代码示例可能无法正常工作。
现在打开该文件并将以下行添加到[dependencies]
Cargo 为您创建的部分标题下方的底部
文件名:Cargo.toml
rand = "0.8.3"
不改动,则不会更新版本。
使用Cargo.lock文件确保可重现的构建
cargo只会用你指定的版本,这个文件就是为了确保它。
更新板条箱
如果你想更新,可以使用下面的命令,它忽略lock文件并写入lock。
$ cargo update
Updating crates.io index
Updating rand v0.8.3 -> v0.8.4
如果有两个新版本,它不会更新到最新的,而是最近的一次更新,如需更新需要自己更改Cargo.toml。
生成随机数
让我们开始使用rand
来生成一个数字来猜测
use rand::Rng;
let secret_number = rand::thread_rng().gen_range(1..=100);
不知道怎么用?Cargo 的另一个巧妙功能是运行该
cargo doc --open
命令将在本地构建所有依赖项提供的文档并在浏览器中打开它。
比较
现在我们有了用户输入和一个随机数,我们可以比较它们。
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
// --snip--
println!("You guessed: {guess}");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
报错了,为什么?以为用户的输入是字符串,无法与数字进行对比。
类型转换
咦,前面不是定义过了吗, 是的你没看过,RUST支持使用使用过的变量。使用该方法可以进行类型转换。
let guess: u32 = guess.trim().parse().expect("Please type a number!");
猜不中?继续猜!
猜不中?我让你猜中为止,循环,欸。
// --snip--
println!("The secret number is: {secret_number}");
loop {
println!("Please input your guess.");
// --snip--
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
}
等下,这猜中了也停不下来啊。优化一下,在猜中的那一条中加入break退出程序
Ordering::Equal => {
println!("You win!");
break;
}
处理一下无效的错误输入,不至于输入一个别的东西就让程序崩溃了。
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
最终程序
大功告成,把随机生成的输出关闭就好啦。
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {guess}");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
当当当当
我一下就猜中了,厉害不
下面要开始学习基础了。