$: cargo new math_game Created binary (application) `math_game` package到此创建完成了,打开项目目录下面的 src/main.rs 源文件并且开始编写程序主体逻辑代码。
fn main() { // 当前回合数 let mut count = 0; // 堆代码 duidaima.com // 需要几轮游戏 let rounds = 10; // 用了存储分数 let mut score = 0; // 生成两个随机数 默认值都为 0 let mut addend: u32 = 0; let mut adding: u32 = 0; }接下来我们要定义两个函数,分别来处理随机数生成和并将随机数转成数学公式展示给用户,下面定义一个函数,在 Rust 中定义一个函数要使用 fn 关键字。在 Rust 因为特殊的内存管理方式,所有权规则,默认在函数之间传递变量可能互导致变量的所有权被转移,所以这里在函数签名中采用的是可变借用的方式,具体什么是所有权规则和借用规则这不是本篇文章的重点,对应 Rust 初学者来说应该关注着怎么写一个完整的小程序然后慢慢深入,函数体如下:
fn next_math(addend: &mut u32, adding: &mut u32) -> u32 { *addend = 0; *adding = 0; // 直接返回2个数的和方便结果判断 *addend + *adding }上面定义一个名为 next_math 的函数,参数列表分别为外部传入的是存储被加数和加数的变量,返回值则是这两个数的之和。目前具体的随机数生成逻辑还没有写,如果需要随机数生成这时我们就使用第三方的 Crates,什么是 Crate ?Crate 可以认为是第三方开发者实现的一些类库,类似于 Java 中的 jar 包,而 Crates 则是一个存放这些工具库的中心仓库,类似于 Java 中央创库 Maven。使用第三方的 rand 库只需要在项目的根目录中 cargo.toml 添加对应的依赖名称:
[dependencies] rand = "0.8.3"添加完成之后,需要使用 cargo update 更新一下项目,更新完成之后如果没有错误,即可去代码中完善 next_math 函数的逻辑,如下:
fn next_math(addend: &mut u32, adding: &mut u32) -> u32 { *addend = rand::thread_rng().gen_range(1..=100); *adding = rand::thread_rng().gen_range(1..=100); // 直接返回2个数的和方便结果判断 *addend + *adding }在函数中使用 rand::thread_rng().gen_range(1..=100) 即可生成 1 至 100 之间自然数,如果需要生成更大的数字只需要调整范围即可,最后如果不写 return 表示最后一行的结果作为返回值,返回值类型为 u32 类型,这里返回值充当着两数之和并且返回给上层调用程序使用。接下来要编写一个提问函数 question 向用户展示计算公式要求计算公式,传入刚才通过 next_math 函数生成的数字,并且传入题目的索引编号,如下:
// 向用户展示计算公式要求计算公式 fn question(addend: &mut u32, adding: &mut u32, index: &mut u32) { println!("🤔: 第{index}题为 {addend} + {adding} = ? 请输入正确结果?", index = *index + 1, addend = addend, adding = adding); }这里我们使用了 println! 这是一个 Rust 内置的宏,可以将字符串格式化输出,在前段字符串中的 {} 则为后面所需要传入的变量名称占位符。接下来我们可以编写程序主体逻辑了,要让用户不断输入信息,必须使用循环控制语句来编写,在 Rust 中有多种循环语句,这里我们使用的是 while 循环语句,当条件满足时会进入循环体执行里面逻辑,当不满足时则跳出了循环体执行剩下的代码逻辑,代码逻辑如下:
use std::cmp::Ordering; use rand::Rng; use std::io; fn main() { // 当前回合数 let mut count = 0; // 需要几轮游戏 let rounds = 10; // 用了存储分数 let mut score = 0; // 生成两个随机数 默认值都为 0 let mut addend: u32 = 0; let mut adding: u32 = 0; println!("😄: 让我们开始游戏吧! 一轮游戏为10个回合,每题10分,总分100分!"); // 这里我们使用 while 制造一个循环 while count < rounds { // 开始生成题目并且出题,将随机数加起来计算总和数 let sum = next_math(&mut addend, &mut adding); // 向控制输出数学问题信息 question(&mut addend, &mut adding, &mut count); // 用来存储用户控制台输入的变量 let mut guess = String::new(); // 从控制台上读入输入字符串 io::stdin() .read_line(&mut guess) .expect("😠: 你能不能好好输!请输入正数!"); // 解析输入的值 let guess: u32 = match guess.trim().parse() { Ok(num) => num, // 如果非法设置默认值 Err(_) => 0, }; match guess.cmp(&sum) { Ordering::Less => { println!("❌: 你的答案太小了!正确答案是 {sum}!", sum = sum); } Ordering::Greater => { println!("❌: 你的答案太大了!正确答案是 {sum}!", sum = sum); } Ordering::Equal => { score += 10; println!("✅: 恭喜你!答案正确!加10分!"); } }; // 添加轮数将其加一 count += 1; } println!("🥳: 本轮游戏结束!你的分数为 {score} !{exp}", score = score, exp = if score >= 60 { "成绩合格!" } else { "成绩不合格!" }) } fn next_math(addend: &mut u32, adding: &mut u32) -> u32 { *addend = rand::thread_rng().gen_range(1..=100); *adding = rand::thread_rng().gen_range(1..=100); // 直接返回2个数的和方便结果判断 *addend + *adding } // 堆代码 duidaima.com // 向用户展示计算公式要求计算结构 fn question(addend: &mut u32, adding: &mut u32, index: &mut u32) { println!("🤔: 第{index}题为 {addend} + {adding} = ? 请输入正确结果?", index = *index + 1, addend = addend, adding = adding); }这里跟要关注的是主体代码逻辑里面函数作用,要让用户输入信息那就必须使用系统调用从控制台中读入输入的数据,使用两段代码逻辑完成,如下:
// 用来存储用户控制台输入的变量 let mut guess = String::new(); // 从控制台上读入输入字符串 io::stdin() .read_line(&mut guess) .expect("😠: 你能不能好好输!请输入正数!");第一段中的 guess 为用来存储临时用户输入的字符串信息,第二段逻辑则是通过标准库中的 io 包从标准输入中读入一行输入数据,而 expect 则防止用户输入的信息是非法,如果非法程序就提示该错误信息,非常容易理解。
// 解析输入的值 let guess: u32 = match guess.trim().parse() { Ok(num) => num, // 如果非法设置默认值 Err(_) => 0, }; match guess.cmp(&sum) { Ordering::Less => { println!("❌: 你的答案太小了!正确答案是 {sum}!", sum = sum); } Ordering::Greater => { println!("❌: 你的答案太大了!正确答案是 {sum}!", sum = sum); } Ordering::Equal => { score += 10; println!("✅: 恭喜你!答案正确!加10分!"); } };最后我们得到用户输入的字符串,这时我们需要将字符串转成数字,将字符串转成数字的方式有很多种,这里使用的具有 Rust 特性的模式匹配的方式,如果字符串转换成功将执行 Ok() 返回正常的数字,而如果字符串不能成功转换成数字则会走 Err(_) ,这里如果转换错误就使用默认值 0 作为用户输入的结果。
最后我们再次使用了模式匹配,将用户输入的结果和计算机计算的结果进行比对,在程序的头部我们增加了另一个 use 声明,从标准库引入了一个叫做 std::cmp::Ordering 的类型到作用域中。 Ordering 也是一个枚举,不过它的成员是 Less、Greater 和 Equal。
这是比较两个值时可能出现的三种结果,接着,底部的五行新代码使用了 Ordering 类型,cmp 方法用来比较两个值并可以在任何可比较的值上调用。它获取一个被比较值的引用:这里是把 guess 与 sum 做比较。 然后它会返回一个刚才通过 use 引入作用域的 Ordering 枚举的成员。使用一个 match 表达式,根据对 guess 和 sum 调用 cmp 返回的 Ordering 成员来决定接下来做什么。
println!( "🥳: 本轮游戏结束!你的分数为 {score} !{exp}", score = score, exp = if score >= 60 { "成绩合格!" } else { "成绩不合格!" } )因为 Rust 没有三目表达式,这里我采用的是 if 语句块来控制具体的分数合格信息的输出。