猜数字游戏
目标:程序在 1 到 100 之间随机取一个整数,循环读取用户猜测,提示"太大/太小",猜中后退出。本课基于一份可运行的完整实现,梳理其中用到的全部知识点。
1. 依赖:引入 rand
随机数来自 crates.io 上最常用的 rand 库,在 Cargo.toml 中声明即可:
toml
[package]
name = "lesson_0002"
version = "0.1.0"
edition.workspace = true
[dependencies]
rand = "0.10.2""0.10.2"遵循语义化版本,cargo 据此下载依赖并锁定到Cargo.lock。- 声明等价于命令
cargo add rand,无需手动编辑文件。
2. 完整实现(注释版)
rust
// 引入第三方 crate rand 的 RngExt trait:random_range 方法由它提供。
// 规则:调用某 trait 的方法前,该 trait 必须在当前作用域内。
use rand::RngExt;
// 一次引入标准库的多个项:{} 内为嵌套路径写法,等价于两行独立的 use。
use std::{cmp::Ordering, io};
fn main() {
println!("猜数字游戏!请输入你的猜测:");
// 生成随机答案:rng() 获取随机数生成器,random_range(1..=100) 取 [1, 100] 区间内的整数(含两端)。
// secret_number 未加 mut:猜数字过程中答案不可变,符合默认不可变约定。
// #region answer
let secret_number = rand::rng().random_range(1..=100);
// #endregion answer
let mut guess_times = 0;
loop {
// 创建可变的空字符串:read_line 需要向其中写入内容,因此必须声明 mut。
// #region read
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("🔴 读取输入失败!");
// #endregion read
// 遮蔽(shadowing):重新声明同名变量 guess,类型从 String 变为 u32。
// trim() 去掉首尾空白(read_line 会带入换行符);
// parse() 把字符串解析为数字,目标类型由左侧的 u32 标注决定;
// parse 同样返回 Result,expect 在解析失败(如输入非数字)时终止程序。
// #region convert
let guess: u32 = guess.trim().parse().expect("🔴 请输入数字!");
// #endregion convert
// match 表达式:对 cmp 的结果逐一匹配分支,必须覆盖所有可能(穷尽性)。
// cmp 比较两个值,返回 Ordering 枚举:Less(小)/ Greater(大)/ Equal(相等)。
// &secret_number 以引用方式传入,不转移所有权(详见后续所有权课程)。
// #region compare
match guess.cmp(&secret_number) {
Ordering::Less => {
guess_times += 1;
println!("🔴 太小了!");
}
Ordering::Greater => {
guess_times += 1;
println!("🔴 太大了!");
}
Ordering::Equal => {
println!("🎉 猜对了!");
println!("猜了 {} 次", guess_times);
break; // 猜中后退出 loop,程序结束
}
}
// #endregion compare
}
}3. 知识点清单
组织与引入
| 知识点 | 说明 |
|---|---|
use 与路径 | 把其他模块/crate 的项引入当前作用域 |
嵌套路径 std::{cmp::Ordering, io} | 一次引入同前缀的多个项,等价于两行 use |
| trait 在作用域内 | random_range 由 RngExt trait 提供;trait 不引入就无法调用其方法 |
fn main() | binary crate 的入口函数,程序从此开始执行 |
依赖管理
| 知识点 | 说明 |
|---|---|
[dependencies] | 声明第三方依赖与版本要求 |
语义化版本 + Cargo.lock | "0.10.2" 是版本要求,实际版本锁定在 lock 文件中 |
读取输入
| 知识点 | 说明 |
|---|---|
String::new() | 创建新的空字符串 |
mut | 变量默认不可变;read_line 要写入,必须声明可变 |
io::stdin().read_line(&mut guess) | 从标准输入读一行,追加写入 guess,包含末尾换行符 |
&mut | 以可变方式借用,不取得所有权(后续所有权课程展开) |
Result 与 expect | read_line 返回 Result;expect 在失败时终止程序并显示消息 |
类型转换
| 知识点 | 说明 |
|---|---|
trim() | 去掉首尾空白,处理 read_line 带入的换行符 |
parse() + 类型标注 u32 | 字符串解析为数字,目标类型由左侧标注决定;返回 Result |
| 遮蔽(shadowing) | let guess: u32 = guess... 重新声明同名变量,类型可从 String 变为 u32 |
随机数
| 知识点 | 说明 |
|---|---|
rand::rng() | 获取当前线程的随机数生成器 |
random_range(1..=100) | 在指定范围内取随机值;..= 表示包含两端 |
控制流
| 知识点 | 说明 |
|---|---|
loop / break | 无限循环;break 退出循环 |
match | 模式匹配表达式,分支必须穷尽所有可能 |
cmp 与 Ordering | 比较两个值,返回 Less / Greater / Equal 枚举 |
&secret_number | 以引用传入,供 cmp 读取而不取得所有权 |