
本章将通过两个完整的实战项目,综合运用之前学到的结构体、枚举、模式匹配等知识,构建实用的状态机和配置解析系统。
通过完成这两个实战项目,你将:
我们要实现一个下载任务状态机,包含以下状态:
Idle:空闲状态Downloading:下载中(需要记录进度)Paused:暂停Completed:完成Failed:失败(需要记录错误信息)状态转换规则:
Idle → Downloading(开始下载)Downloading → Paused(暂停下载)Downloading → Completed(下载完成)Downloading → Failed(下载失败)Paused → Downloading(恢复下载)Paused → Failed(取消下载)
#[derive(Debug, Clone, Copy, PartialEq)]
enum DownloadState {
Idle,
Downloading,
Paused,
Completed,
Failed,
}
#[derive(Debug, Clone)]
struct DownloadTask {
id: u32,
url: String,
state: DownloadState,
progress: f64, // 0.0 到 1.0
error_message: Option<String>,
}
impl DownloadTask {
fn new(id: u32, url: String) -> Self {
DownloadTask {
id,
url,
state: DownloadState::Idle,
progress: 0.0,
error_message: None,
}
}
}impl DownloadTask {
// 开始下载
fn start(&mut self) -> Result<(), String> {
match self.state {
DownloadState::Idle => {
self.state = DownloadState::Downloading;
self.progress = 0.0;
println!("[任务 {}] 开始下载: {}", self.id, self.url);
Ok(())
}
_ => Err(format!("无法从 {:?} 状态开始下载", self.state)),
}
}
// 暂停下载
fn pause(&mut self) -> Result<(), String> {
match self.state {
DownloadState::Downloading => {
self.state = DownloadState::Paused;
println!("[任务 {}] 已暂停,进度: {:.1}%", self.id, self.progress * 100.0);
Ok(())
}
_ => Err(format!("无法从 {:?} 状态暂停", self.state)),
}
}
// 恢复下载
fn resume(&mut self) -> Result<(), String> {
match self.state {
DownloadState::Paused => {
self.state = DownloadState::Downloading;
println!("[任务 {}] 恢复下载,当前进度: {:.1}%", self.id, self.progress * 100.0);
Ok(())
}
_ => Err(format!("无法从 {:?} 状态恢复", self.state)),
}
}
// 更新进度
fn update_progress(&mut self, progress: f64) -> Result<(), String> {
match self.state {
DownloadState::Downloading => {
if progress < 0.0 || progress > 1.0 {
return Err("进度必须在 0.0 到 1.0 之间".to_string());
}
self.progress = progress;
// 如果进度达到 100%,自动完成
if progress >= 1.0 {
self.complete();
}
Ok(())
}
_ => Err(format!("无法在 {:?} 状态更新进度", self.state)),
}
}
// 完成下载
fn complete(&mut self) {
self.state = DownloadState::Completed;
self.progress = 1.0;
println!("[任务 {}] 下载完成!", self.id);
}
// 失败
fn fail(&mut self, error: String) -> Result<(), String> {
match self.state {
DownloadState::Downloading | DownloadState::Paused => {
self.state = DownloadState::Failed;
self.error_message = Some(error.clone());
println!("[任务 {}] 下载失败: {}", self.id, error);
Ok(())
}
_ => Err(format!("无法从 {:?} 状态标记失败", self.state)),
}
}
// 取消下载
fn cancel(&mut self) -> Result<(), String> {
match self.state {
DownloadState::Paused => {
self.state = DownloadState::Failed;
self.error_message = Some("用户取消".to_string());
println!("[任务 {}] 已取消", self.id);
Ok(())
}
_ => Err(format!("无法从 {:?} 状态取消", self.state)),
}
}
// 重置状态
fn reset(&mut self) {
self.state = DownloadState::Idle;
self.progress = 0.0;
self.error_message = None;
println!("[任务 {}] 已重置", self.id);
}
// 获取状态信息
fn status(&self) -> String {
match self.state {
DownloadState::Idle => "空闲".to_string(),
DownloadState::Downloading => format!("下载中 ({:.1}%)", self.progress * 100.0),
DownloadState::Paused => format!("已暂停 ({:.1}%)", self.progress * 100.0),
DownloadState::Completed => "已完成".to_string(),
DownloadState::Failed => {
format!("失败: {}", self.error_message.as_ref().unwrap_or(&"未知错误".to_string()))
}
}
}
}
fn main() {
let mut task = DownloadTask::new(1, "https://example.com/file.zip".to_string());
println!("=== 下载任务状态机演示 ===\n");
// 开始下载
task.start().unwrap();
println!("状态: {}\n", task.status());
// 更新进度
task.update_progress(0.3).unwrap();
println!("状态: {}\n", task.status());
// 暂停
task.pause().unwrap();
println!("状态: {}\n", task.status());
// 恢复
task.resume().unwrap();
task.update_progress(0.6).unwrap();
println!("状态: {}\n", task.status());
// 继续更新到完成
task.update_progress(1.0).unwrap();
println!("状态: {}\n", task.status());
// 重置并演示失败场景
println!("=== 失败场景演示 ===\n");
task.reset();
task.start().unwrap();
task.update_progress(0.5).unwrap();
task.fail("网络连接断开".to_string()).unwrap();
println!("状态: {}\n", task.status());
// 演示无效转换
println!("=== 无效转换演示 ===\n");
match task.start() {
Ok(_) => {}
Err(e) => println!("错误: {}", e),
}
}=== 下载任务状态机演示 ===
[任务 1] 开始下载: https://example.com/file.zip
状态: 下载中 (0.0%)
状态: 下载中 (30.0%)
[任务 1] 已暂停,进度: 30.0%
状态: 已暂停 (30.0%)
[任务 1] 恢复下载,当前进度: 30.0%
状态: 下载中 (60.0%)
[任务 1] 下载完成!
状态: 已完成
=== 失败场景演示 ===
[任务 1] 已重置
[任务 1] 开始下载: https://example.com/file.zip
[任务 1] 下载失败: 网络连接断开
状态: 失败: 网络连接断开
=== 无效转换演示 ===
错误: 无法从 Failed 状态开始下载我们可以创建一个管理器来管理多个下载任务:
use std::collections::HashMap;
struct DownloadManager {
tasks: HashMap<u32, DownloadTask>,
next_id: u32,
}
impl DownloadManager {
fn new() -> Self {
DownloadManager {
tasks: HashMap::new(),
next_id: 1,
}
}
fn add_task(&mut self, url: String) -> u32 {
let id = self.next_id;
self.next_id += 1;
let task = DownloadTask::new(id, url);
self.tasks.insert(id, task);
id
}
fn get_task(&mut self, id: u32) -> Option<&mut DownloadTask> {
self.tasks.get_mut(&id)
}
fn list_tasks(&self) {
println!("=== 下载任务列表 ===");
for (id, task) in &self.tasks {
println!("任务 {}: {}", id, task.status());
}
}
fn remove_task(&mut self, id: u32) -> bool {
self.tasks.remove(&id).is_some()
}
}
fn main() {
let mut manager = DownloadManager::new();
let id1 = manager.add_task("https://example.com/file1.zip".to_string());
let id2 = manager.add_task("https://example.com/file2.zip".to_string());
manager.list_tasks();
if let Some(task) = manager.get_task(id1) {
task.start().unwrap();
task.update_progress(0.5).unwrap();
}
println!("\n更新后:");
manager.list_tasks();
}我们要实现一个灵活的配置系统,支持:

use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
enum ConfigValue {
String(String),
Number(i32),
Float(f64),
Boolean(bool),
List(Vec<String>),
}
#[derive(Debug)]
struct Config {
values: HashMap<String, ConfigValue>,
}
impl Config {
fn new() -> Self {
Config {
values: HashMap::new(),
}
}
// 设置配置值
fn set(&mut self, key: String, value: ConfigValue) {
self.values.insert(key, value);
}
// 获取字符串值
fn get_string(&self, key: &str) -> Option<&String> {
match self.values.get(key) {
Some(ConfigValue::String(s)) => Some(s),
_ => None,
}
}
// 获取字符串值(带默认值)
fn get_string_or(&self, key: &str, default: &str) -> String {
self.get_string(key)
.map(|s| s.clone())
.unwrap_or(default.to_string())
}
// 获取数字值
fn get_number(&self, key: &str) -> Option<i32> {
match self.values.get(key) {
Some(ConfigValue::Number(n)) => Some(*n),
_ => None,
}
}
// 获取数字值(带默认值)
fn get_number_or(&self, key: &str, default: i32) -> i32 {
self.get_number(key).unwrap_or(default)
}
// 获取浮点数值
fn get_float(&self, key: &str) -> Option<f64> {
match self.values.get(key) {
Some(ConfigValue::Float(f)) => Some(*f),
_ => None,
}
}
// 获取布尔值
fn get_boolean(&self, key: &str) -> Option<bool> {
match self.values.get(key) {
Some(ConfigValue::Boolean(b)) => Some(*b),
_ => None,
}
}
// 获取列表值
fn get_list(&self, key: &str) -> Option<&Vec<String>> {
match self.values.get(key) {
Some(ConfigValue::List(list)) => Some(list),
_ => None,
}
}
// 检查配置是否存在
fn has(&self, key: &str) -> bool {
self.values.contains_key(key)
}
// 获取所有键
fn keys(&self) -> Vec<&String> {
self.values.keys().collect()
}
// 移除配置项
fn remove(&mut self, key: &str) -> Option<ConfigValue> {
self.values.remove(key)
}
}#[derive(Debug)]
enum ConfigError {
MissingKey(String),
TypeMismatch { key: String, expected: String, got: String },
InvalidValue { key: String, message: String },
}
impl Config {
// 验证必需的配置项
fn require_string(&self, key: &str) -> Result<&String, ConfigError> {
self.get_string(key)
.ok_or_else(|| ConfigError::MissingKey(key.to_string()))
}
// 验证数字范围
fn require_number_in_range(
&self,
key: &str,
min: i32,
max: i32,
) -> Result<i32, ConfigError> {
let value = self.get_number(key)
.ok_or_else(|| ConfigError::MissingKey(key.to_string()))?;
if value < min || value > max {
Err(ConfigError::InvalidValue {
key: key.to_string(),
message: format!("值 {} 必须在 {} 到 {} 之间", value, min, max),
})
} else {
Ok(value)
}
}
// 验证列表非空
fn require_non_empty_list(&self, key: &str) -> Result<&Vec<String>, ConfigError> {
let list = self.get_list(key)
.ok_or_else(|| ConfigError::MissingKey(key.to_string()))?;
if list.is_empty() {
Err(ConfigError::InvalidValue {
key: key.to_string(),
message: "列表不能为空".to_string(),
})
} else {
Ok(list)
}
}
}struct ConfigBuilder {
config: Config,
}
impl ConfigBuilder {
fn new() -> Self {
ConfigBuilder {
config: Config::new(),
}
}
fn with_string(mut self, key: &str, value: String) -> Self {
self.config.set(key.to_string(), ConfigValue::String(value));
self
}
fn with_number(mut self, key: &str, value: i32) -> Self {
self.config.set(key.to_string(), ConfigValue::Number(value));
self
}
fn with_float(mut self, key: &str, value: f64) -> Self {
self.config.set(key.to_string(), ConfigValue::Float(value));
self
}
fn with_boolean(mut self, key: &str, value: bool) -> Self {
self.config.set(key.to_string(), ConfigValue::Boolean(value));
self
}
fn with_list(mut self, key: &str, value: Vec<String>) -> Self {
self.config.set(key.to_string(), ConfigValue::List(value));
self
}
fn build(self) -> Config {
self.config
}
}fn main() {
println!("=== 配置系统演示 ===\n");
// 方式1:直接设置
let mut config1 = Config::new();
config1.set("name".to_string(), ConfigValue::String("Alice".to_string()));
config1.set("age".to_string(), ConfigValue::Number(25));
config1.set("active".to_string(), ConfigValue::Boolean(true));
println!("配置1:");
println!(" 姓名: {}", config1.get_string_or("name", "未知"));
println!(" 年龄: {}", config1.get_number_or("age", 0));
println!(" 激活: {}", config1.get_boolean("active").unwrap_or(false));
// 方式2:使用构建器
let config2 = ConfigBuilder::new()
.with_string("server", "localhost".to_string())
.with_number("port", 8080)
.with_float("timeout", 30.5)
.with_boolean("ssl", true)
.with_list("hosts", vec![
"example.com".to_string(),
"test.com".to_string(),
])
.build();
println!("\n配置2:");
println!(" 服务器: {}", config2.get_string_or("server", ""));
println!(" 端口: {}", config2.get_number_or("port", 80));
println!(" 超时: {}", config2.get_float("timeout").unwrap_or(0.0));
println!(" SSL: {}", config2.get_boolean("ssl").unwrap_or(false));
if let Some(hosts) = config2.get_list("hosts") {
println!(" 主机列表: {:?}", hosts);
}
// 配置验证
println!("\n=== 配置验证 ===");
match config2.require_string("server") {
Ok(server) => println!("✓ 服务器配置有效: {}", server),
Err(e) => println!("✗ 错误: {:?}", e),
}
match config2.require_number_in_range("port", 1, 65535) {
Ok(port) => println!("✓ 端口配置有效: {}", port),
Err(e) => println!("✗ 错误: {:?}", e),
}
// 演示验证失败
let mut config3 = Config::new();
config3.set("port".to_string(), ConfigValue::Number(99999));
match config3.require_number_in_range("port", 1, 65535) {
Ok(port) => println!("✓ 端口: {}", port),
Err(e) => println!("✗ 错误: {:?}", e),
}
}impl Config {
// 从字符串解析配置(简单格式:key=value)
fn from_string(s: &str) -> Result<Config, String> {
let mut config = Config::new();
for line in s.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue; // 跳过空行和注释
}
if let Some((key, value)) = line.split_once('=') {
let key = key.trim().to_string();
let value = value.trim();
// 尝试解析为不同的类型
if let Ok(num) = value.parse::<i32>() {
config.set(key, ConfigValue::Number(num));
} else if let Ok(float) = value.parse::<f64>() {
config.set(key, ConfigValue::Float(float));
} else if let Ok(boolean) = value.parse::<bool>() {
config.set(key, ConfigValue::Boolean(boolean));
} else {
config.set(key, ConfigValue::String(value.to_string()));
}
}
}
Ok(config)
}
}
fn main() {
let config_text = r#"
# 服务器配置
server=localhost
port=8080
timeout=30.5
ssl=true
# 主机列表(需要特殊处理)
"#;
match Config::from_string(config_text) {
Ok(config) => {
println!("解析成功:");
for key in config.keys() {
println!(" {}: {:?}", key, config.values.get(key));
}
}
Err(e) => println!("解析失败: {}", e),
}
}
结合状态机和配置系统,实现一个HTTP客户端状态机,支持:
use std::collections::HashMap;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq)]
enum ConnectionState {
Idle,
Connecting,
Connected,
Disconnected,
Error,
}
#[derive(Debug, Clone)]
struct HttpClient {
state: ConnectionState,
config: Config,
error_message: Option<String>,
connection_count: u32,
}
impl HttpClient {
fn new(config: Config) -> Self {
HttpClient {
state: ConnectionState::Idle,
config,
error_message: None,
connection_count: 0,
}
}
fn connect(&mut self) -> Result<(), String> {
match self.state {
ConnectionState::Idle | ConnectionState::Disconnected => {
self.state = ConnectionState::Connecting;
println!("正在连接到服务器...");
// 模拟连接过程
let server = self.config.get_string_or("server", "localhost");
let timeout = self.config.get_number_or("timeout", 30);
println!(" 服务器: {}", server);
println!(" 超时: {} 秒", timeout);
// 模拟连接成功
self.state = ConnectionState::Connected;
self.connection_count += 1;
println!("连接成功!");
Ok(())
}
ConnectionState::Connected => {
println!("已经连接");
Ok(())
}
ConnectionState::Connecting => {
Err("正在连接中,请稍候".to_string())
}
ConnectionState::Error => {
// 从错误状态可以重试
self.state = ConnectionState::Connecting;
self.error_message = None;
self.connect()
}
}
}
fn disconnect(&mut self) -> Result<(), String> {
match self.state {
ConnectionState::Connected => {
self.state = ConnectionState::Disconnected;
println!("已断开连接");
Ok(())
}
ConnectionState::Connecting => {
self.state = ConnectionState::Idle;
println!("取消连接");
Ok(())
}
_ => Err(format!("无法从 {:?} 状态断开", self.state)),
}
}
fn send_request(&mut self, path: &str) -> Result<String, String> {
match self.state {
ConnectionState::Connected => {
let server = self.config.get_string_or("server", "localhost");
println!("发送请求: {}/{}", server, path);
Ok(format!("响应: GET {}/{} 200 OK", server, path))
}
_ => Err("未连接,无法发送请求".to_string()),
}
}
fn handle_error(&mut self, error: String) {
self.state = ConnectionState::Error;
self.error_message = Some(error);
println!("发生错误: {}", self.error_message.as_ref().unwrap());
}
fn reset(&mut self) {
self.state = ConnectionState::Idle;
self.error_message = None;
println!("重置客户端");
}
fn status(&self) -> String {
match self.state {
ConnectionState::Idle => "空闲".to_string(),
ConnectionState::Connecting => "连接中...".to_string(),
ConnectionState::Connected => {
format!("已连接 (连接次数: {})", self.connection_count)
}
ConnectionState::Disconnected => "已断开".to_string(),
ConnectionState::Error => {
format!("错误: {}",
self.error_message.as_ref().unwrap_or(&"未知错误".to_string()))
}
}
}
}
fn main() {
println!("=== HTTP客户端状态机演示 ===\n");
// 创建配置
let config = ConfigBuilder::new()
.with_string("server", "api.example.com".to_string())
.with_number("port", 443)
.with_number("timeout", 30)
.with_boolean("ssl", true)
.build();
// 创建客户端
let mut client = HttpClient::new(config);
println!("初始状态: {}\n", client.status());
// 连接
client.connect().unwrap();
println!("状态: {}\n", client.status());
// 发送请求
match client.send_request("api/users") {
Ok(response) => println!("{}\n", response),
Err(e) => println!("错误: {}\n", e),
}
// 断开连接
client.disconnect().unwrap();
println!("状态: {}\n", client.status());
// 重新连接
client.connect().unwrap();
println!("状态: {}\n", client.status());
// 模拟错误
client.handle_error("网络超时".to_string());
println!("状态: {}\n", client.status());
// 从错误状态重试
client.connect().unwrap();
println!("状态: {}\n", client.status());
}get_or 方法支持默认值Result<T, E>? 运算符传播错误创建一个文件上传状态机,包含以下状态:
Pending:等待上传Uploading:上传中(记录进度)Paused:暂停Completed:完成Failed:失败实现状态转换和进度更新功能。
创建一个数据库连接池配置系统,包含:
实现配置验证,确保配置项之间的约束关系正确。
创建一个游戏状态机,包含:
Menu:主菜单Playing:游戏中Paused:暂停GameOver:游戏结束Settings:设置实现状态转换,并添加配置系统管理游戏设置(难度、音效、画面质量等)。
创建一个日志系统配置,支持:
实现配置验证和日志系统状态机。
通过本章的学习,我们完成了三个实战项目:
这些项目展示了 Rust 中结构体和枚举的强大组合,以及如何通过模式匹配实现清晰的业务逻辑。