第七章:高级特性与项目优化

第七章:高级特性与项目优化

教学目标

  • 掌握 Rust 命令行参数解析库 clap 的使用,实现更专业的命令行接口
  • 理解异步编程模型,掌握 tokio 库的 async/await 语法与异步操作
  • 掌握 Rust 并发编程基础,包括线程创建、共享数据安全访问
  • 运用高级特性优化 RustTask 项目,提升用户体验与系统性能

核心知识点

1. 命令行参数解析

clap 库基础用法

clap 是 Rust 中最流行的命令行解析库,支持复杂的命令行接口设计,包括子命令、参数验证、帮助信息生成等。

rust 复制代码
# Cargo.toml
[dependencies]
clap = { version = "4.4", features = ["derive"] }
rust 复制代码
use clap::Parser;
// 使用derive宏定义命令行参数结构
#[derive(Parser, Debug)]
#[clap(author, version, about = "RustTask - 命令行任务管理工具")]
struct Args {
    /// 任务标题
    #[clap(short, long)]
    title: String,
    
    /// 任务描述
    #[clap(short, long)]
    description: String,
    
    /// 截止日期,格式为YYYY-MM-DD
    #[clap(short, long, default_value = "today")]
    due: String,
    
    /// 显示详细信息
    #[clap(short, long)]
    verbose: bool,
}
fn main() {
    let args = Args::parse();
    
    println!("任务标题: {}", args.title);
    println!("任务描述: {}", args.description);
    println!("截止日期: {}", args.due);
    println!("详细模式: {}", args.verbose);
}
子命令与命令行层级结构

clap 支持定义子命令,使命令行接口更清晰,适合复杂的应用程序。

rust 复制代码
use clap::Parser;
#[derive(Parser, Debug)]
#[clap(author, version, about = "RustTask - 命令行任务管理工具")]
enum Command {
    /// 添加新任务
    Add {
        title: String,
        description: String,
        #[clap(short, long, default_value = "today")]
        due: String,
    },
    /// 列出所有任务
    List {
        #[clap(short, long, possible_values = &["todo", "in-progress", "completed"])]
        status: Option<String>,
    },
    /// 更新任务状态
    Update {
        id: u32,
        #[clap(short, long, possible_values = &["todo", "in-progress", "completed"])]
        status: String,
    },
    /// 删除任务
    Delete {
        id: u32,
        #[clap(short, long, default_value = "false")]
        force: bool,
    },
}
fn main() {
    let command = Command::parse();
    
    match command {
        Command::Add { title, description, due } => {
            println!("添加任务: {} - {}", title, description);
            println!("截止日期: {}", due);
        },
        Command::List { status } => {
            println!("列出任务");
            if let Some(status) = status {
                println!("筛选状态: {}", status);
            }
        },
        Command::Update { id, status } => {
            println!("更新任务 {} 状态为: {}", id, status);
        },
        Command::Delete { id, force } => {
            if force {
                println!("强制删除任务 {}", id);
            } else {
                println!("确认删除任务 {} (y/n)?", id);
                // 处理确认逻辑
            }
        },
    }
}
自定义帮助信息与验证

clap 支持自定义帮助信息和参数验证,提升用户体验。

rust 复制代码
use clap::Parser;
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[clap(
    author = "RustTask 团队",
    version = "1.0.0",
    about = "高效的命令行任务管理工具",
    long_about = None, // 不显示详细帮助
)]
struct Args {
    /// 配置文件路径
    #[clap(short, long, value_parser, default_value = "config.toml")]
    config: PathBuf,
    
    /// 日志级别
    #[clap(
        short, 
        long, 
        value_enum,
        possible_values = &["debug", "info", "warn", "error"],
        default_value = "info"
    )]
    log_level: String,
    
    /// 运行模式
    #[clap(
        short, 
        long, 
        value_parser,
        possible_values = &["interactive", "batch", "daemon"],
        required = true
    )]
    mode: String,
}
// 自定义参数验证
fn validate_mode(mode: &str) -> Result<(), String> {
    match mode {
        "interactive" | "batch" | "daemon" => Ok(()),
        _ => Err("无效的运行模式,必须为 interactive、batch 或 daemon".to_string()),
    }
}
fn main() {
    let args = Args::parse();
    
    println!("配置文件: {}", args.config.display());
    println!("日志级别: {}", args.log_level);
    println!("运行模式: {}", args.mode);
}

2. 异步编程

tokio 运行时与异步函数

tokio 是 Rust 中最常用的异步运行时,支持非阻塞 IO 和异步任务调度。

rust 复制代码
# Cargo.toml
[dependencies]
tokio = { version = "1.32", features = ["full"] }
rust 复制代码
use tokio::time::{sleep, Duration};
// 异步函数,必须在异步上下文中调用
async fn async_greeting() {
    println!("开始异步任务");
    // 非阻塞睡眠,不阻塞线程
    sleep(Duration::from_secs(1)).await;
    println!("异步任务完成");
}
fn main() {
    // 运行异步函数
    tokio::runtime::Runtime::new()
        .unwrap()
        .block_on(async_greeting());
    
    println!("主线程继续执行");
}
async/await 语法与 Future

async/await 是 Rust 异步编程的核心语法,允许编写类似同步的异步代码。

rust 复制代码
use tokio::time::{sleep, Duration};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::net::TcpListener;
// 异步函数返回Future
async fn fetch_data() -> Result<String, Box<dyn std::error::Error>> {
    // 模拟网络请求
    sleep(Duration::from_secs(1)).await;
    Ok("数据已获取".to_string())
}
// 多个异步任务并发执行
async fn concurrent_tasks() -> Result<(), Box<dyn std::error::Error>> {
    // 创建两个异步任务
    let task1 = fetch_data();
    let task2 = fetch_data();
    
    // 并发执行任务
    let result1 = task1.await?;
    let result2 = task2.await?;
    
    println!("任务1结果: {}", result1);
    println!("任务2结果: {}", result2);
    Ok(())
}
// 异步TCP服务器
async fn tcp_server() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
    println!("服务器启动,监听127.0.0.1:8080");
    
    loop {
        let (mut socket, _) = listener.accept().await?;
        println!("新连接已建立");
        
        // 处理客户端连接
        tokio::spawn(async move {
            let mut buffer = [0; 1024];
            if let Ok(bytes_read) = socket.read(&mut buffer).await {
                if bytes_read > 0 {
                    println!("收到数据: {:?}", &buffer[0..bytes_read]);
                    socket.write_all(b"Hello from Rust!").await.unwrap();
                }
            }
        });
    }
}
异步文件操作与流处理

tokio 提供了异步文件操作 API,适合处理 IO 密集型任务。

rust 复制代码
use tokio::fs::{read_to_string, write};
use tokio::stream::StreamExt;
// 异步读取文件
async fn read_file(path: &str) -> Result<String, std::io::Error> {
    read_to_string(path).await
}
// 异步写入文件
async fn write_file(path: &str, content: &str) -> Result<(), std::io::Error> {
    write(path, content).await
}
// 处理文件流
async fn process_files() -> Result<(), std::io::Error> {
    // 读取多个文件并处理
    let file1 = read_file("file1.txt").await?;
    let file2 = read_file("file2.txt").await?;
    
    let result = file1 + &file2;
    write_file("combined.txt", &result).await?;
    
    println!("文件处理完成");
    Ok(())
}
// 处理数据流
async fn process_stream() {
    // 创建一个包含1-10的流
    let stream = tokio::stream::iter(1..=10);
    
    // 异步遍历流
    let mut stream = stream;
    while let Some(item) = stream.next().await {
        println!("处理项目: {}", item);
        // 模拟处理时间
        tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
    }
}

3. 并发编程

线程创建与管理

Rust 的标准库提供了轻量级线程,支持跨平台的线程创建与管理。

rust 复制代码
use std::thread;
use std::time::Duration;
fn main() {
    // 创建新线程
    let thread = thread::spawn(|| {
        println!("子线程开始执行");
        thread::sleep(Duration::from_secs(1));
        println!("子线程执行完毕");
    });
    
    println!("主线程继续执行");
    
    // 等待子线程完成
    thread.join().unwrap();
    println!("所有线程执行完毕");
}
// 带返回值的线程
fn thread_with_result() {
    let result = thread::spawn(|| {
        println!("计算中...");
        thread::sleep(Duration::from_secs(1));
        10 * 2
    }).join().unwrap();
    
    println!("计算结果: {}", result);
}
// 线程局部存储
fn thread_local_storage() {
    thread_local! {
        static COUNTER: u32 = 0;
    }
    
    // 多个线程共享thread_local变量
    let handles = (0..5).map(|i| {
        thread::spawn(move || {
            COUNTER.with(|c| {
                *c += 1;
                println!("线程 {}: COUNTER = {}", i, c);
            });
        })
    }).collect::<Vec<_>>();
    
    for handle in handles {
        handle.join().unwrap();
    }
}
共享数据并发访问

Rust 提供了Mutex和Arc用于安全地在多个线程间共享可变数据。

rust 复制代码
use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
    // Arc<Mutex<T>> 用于在多个线程间共享可变数据
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    for i in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut count = counter.lock().unwrap();
            *count += 1;
            println!("线程 {}: 计数器 = {}", i, *count);
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("最终计数器值: {}", *counter.lock().unwrap());
}
// 读写锁示例
fn read_write_lock() {
    use std::sync::{RwLock, Arc};
    use std::thread;
    
    let data = Arc::new(RwLock::new(String::from("初始数据")));
    let mut handles = vec![];
    
    // 创建10个读线程
    for i in 0..10 {
        let data = Arc::clone(&data);
        let handle = thread::spawn(move || {
            let read_lock = data.read().unwrap();
            println!("读线程 {}: 数据 = {}", i, read_lock);
        });
        handles.push(handle);
    }
    
    // 创建1个写线程
    let data = Arc::clone(&data);
    let handle = thread::spawn(move || {
        let mut write_lock = data.write().unwrap();
        *write_lock = "更新后的数据".to_string();
        println!("写线程: 数据已更新");
    });
    handles.push(handle);
    
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("最终数据: {}", *data.read().unwrap());
}
通道与线程间通信

mpsc(多生产者单消费者)通道用于线程间安全地传递数据。

rust 复制代码
use std::thread;
use std::time::Duration;
use std::sync::mpsc;
fn main() {
    // 创建通道
    let (sender, receiver) = mpsc::channel();
    
    // 生产者线程
    let producer = thread::spawn(move || {
        for i in 0..5 {
            sender.send(i).unwrap();
            println!("生产者发送: {}", i);
            thread::sleep(Duration::from_secs(1));
        }
    });
    
    // 消费者线程
    let consumer = thread::spawn(move || {
        for message in receiver {
            println!("消费者接收: {}", message);
        }
    });
    
    // 等待生产者完成
    producer.join().unwrap();
    // 关闭通道,消费者将退出
    drop(sender);
    consumer.join().unwrap();
}
// 多生产者单消费者通道
fn multiple_producers() {
    use std::sync::mpsc;
    use std::thread;
    use std::time::Duration;
    
    let (sender, receiver) = mpsc::channel();
    let senders = (0..3).map(|i| {
        let sender = mpsc::Sender::clone(&sender);
        thread::spawn(move || {
            for j in 0..3 {
                sender.send((i, j)).unwrap();
                thread::sleep(Duration::from_millis(500));
            }
        })
    }).collect::<Vec<_>>();
    
    // 消费所有消息
    for _ in 0..9 {
        if let Ok((producer, message)) = receiver.recv() {
            println!("接收来自生产者 {} 的消息: {}", producer, message);
        }
    }
    
    // 等待所有生产者完成
    for sender in senders {
        sender.join().unwrap();
    }
}
// 异步通道
async fn async_channel() {
    use tokio::sync::mpsc;
    use tokio::time::Duration;
    
    // 创建异步通道
    let (sender, mut receiver) = mpsc::channel(10);
    
    // 生产者任务
    tokio::spawn(async move {
        for i in 0..5 {
            sender.send(i).await.unwrap();
            println!("异步生产者发送: {}", i);
            tokio::time::sleep(Duration::from_secs(1)).await;
        }
    });
    
    // 消费者任务
    tokio::spawn(async move {
        while let Some(message) = receiver.recv().await {
            println!("异步消费者接收: {}", message);
        }
    });
    
    // 等待一段时间
    tokio::time::sleep(Duration::from_secs(6)).await;
}

项目实战:RustTask 高级特性优化

1. 用 clap 重构命令行界面

使用 clap 库重新设计 RustTask 的命令行接口,实现更专业的命令行交互。

rust 复制代码
// src/main.rs
mod task;
mod cli;
mod storage;
use clap::Parser;
use cli::TaskManager;
use storage::{FileStorage, TaskStorage};
use task::TaskStatus;
use std::path::PathBuf;
use std::time::SystemTime;
// 定义命令行子命令
#[derive(Parser, Debug)]
#[clap(author, version, about = "RustTask - 高效的命令行任务管理工具")]
enum Command {
    /// 添加新任务
    Add {
        /// 任务标题
        #[clap(short, long)]
        title: String,
        /// 任务描述
        #[clap(short, long)]
        description: String,
        /// 截止日期 (YYYY-MM-DD)
        #[clap(short, long, default_value = "today")]
        due: String,
    },
    /// 列出所有任务
    List {
        /// 按状态筛选
        #[clap(short, long, possible_values = &["todo", "in-progress", "completed"])]
        status: Option<String>,
    },
    /// 更新任务状态
    Update {
        /// 任务ID
        #[clap(short, long)]
        id: u32,
        /// 新状态
        #[clap(short, long, possible_values = &["todo", "in-progress", "completed"])]
        status: String,
    },
    /// 删除任务
    Delete {
        /// 任务ID
        #[clap(short, long)]
        id: u32,
        /// 强制删除,不提示确认
        #[clap(short, long, default_value = "false")]
        force: bool,
    },
}
fn main() {
    // 解析命令行参数
    let command = Command::parse();
    let storage_path = PathBuf::from("tasks.json");
    let storage = FileStorage::new(storage_path.clone());
    
    // 初始化任务管理器
    let mut manager = match TaskManager::new(storage) {
        Ok(manager) => manager,
        Err(e) => {
            eprintln!("初始化任务管理器失败: {}", e);
            return;
        }
    };
    
    // 处理不同命令
    match command {
        Command::Add { title, description, due } => {
            // 解析截止日期(简化处理)
            let due_date = match due.as_str() {
                "today" => SystemTime::now() + std::time::Duration::from_secs(86400),
                _ => {
                    // 实际项目中应解析日期字符串
                    SystemTime::now() + std::time::Duration::from_secs(86400)
                }
            };
            
            manager.add_task(title, description, due_date);
            if let Err(e) = manager.save() {
                eprintln!("保存任务失败: {}", e);
            }
        },
        Command::List { status } => {
            manager.list_tasks();
        },
        Command::Update { id, status } => {
            let status = match status.as_str() {
                "todo" => TaskStatus::Todo,
                "in-progress" => TaskStatus::InProgress,
                "completed" => TaskStatus::Completed,
                _ => {
                    eprintln!("无效状态");
                    return;
                }
            };
            
            manager.update_task_status(id, status);
            if let Err(e) = manager.save() {
                eprintln!("保存任务失败: {}", e);
            }
        },
        Command::Delete { id, force } => {
            if !force {
                println!("确认删除任务 {}? (y/n)", id);
                let mut input = String::new();
                std::io::stdin().read_line(&mut input).unwrap();
                if input.trim() != "y" {
                    println!("取消删除");
                    return;
                }
            }
            
            if let Err(e) = manager.remove_task(id) {
                eprintln!("删除任务失败: {}", e);
            } else {
                println!("任务已删除");
                if let Err(e) = manager.save() {
                    eprintln!("保存任务失败: {}", e);
                }
            }
        },
    }
}

2. 实现异步文件存储

使用 tokio 库重构存储模块,实现异步文件操作,提升 IO 性能。

rust 复制代码
// src/storage.rs
use crate::task::{Task, TaskStatus};
use std::collections::HashMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tokio::fs::{File, OpenOptions};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::error::Error;
// 定义存储接口
pub trait TaskStorage {
    type Error: std::error::Error;
    async fn save(&self, tasks: &HashMap<u32, Task>, next_id: u32) -> Result<(), Self::Error>;
    async fn load(&self) -> Result<(HashMap<u32, Task>, u32), Self::Error>;
}
// 存储错误类型
#[derive(Error, Debug)]
pub enum StorageError {
    #[error("文件操作错误: {0}")]
    FileError(#[from] tokio::io::Error),
    #[error("JSON解析错误: {0}")]
    JsonError(#[from] serde_json::Error),
    #[error("任务数据格式错误: {0}")]
    DataFormatError(String),
}
// 序列化任务数据
#[derive(Serialize, Deserialize, Debug)]
struct TaskStorageData {
    tasks: HashMap<u32, Task>,
    next_id: u32,
}
// 异步文件存储实现
pub struct AsyncFileStorage {
    path: PathBuf,
}
impl AsyncFileStorage {
    pub fn new(path: impl Into<PathBuf>) -> Self {
        AsyncFileStorage { path: path.into() }
    }
}
impl TaskStorage for AsyncFileStorage {
    type Error = StorageError;
    
    async fn save(&self, tasks: &HashMap<u32, Task>, next_id: u32) -> Result<(), Self::Error> {
        let storage = TaskStorageData {
            tasks: tasks.clone(),
            next_id,
        };
        
        // 序列化为JSON
        let data = serde_json::to_vec(&storage)?;
        
        // 异步写入文件
        let mut file = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open(&self.path)
            .await?;
        file.write_all(&data).await?;
        
        Ok(())
    }
    
    async fn load(&self) -> Result<(HashMap<u32, Task>, u32), Self::Error> {
        // 文件不存在时返回空集合
        if !self.path.exists() {
            return Ok((HashMap::new(), 1));
        }
        
        // 异步读取文件
        let mut file = File::open(&self.path).await?;
        let mut data = Vec::new();
        file.read_to_end(&mut data).await?;
        
        // 反序列化JSON
        let storage: TaskStorageData = serde_json::from_slice(&data)?;
        
        Ok((storage.tasks, storage.next_id))
    }
}

3. 添加任务到期提醒的并发功能

使用线程和通道实现任务到期提醒功能。

rust 复制代码
// src/cli.rs
use crate::storage::TaskStorage;
use crate::task::{Task, TaskStatus};
use std::collections::HashMap;
use std::time::{SystemTime, Duration};
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc;
pub struct TaskManager<S> {
    tasks: Arc<Mutex<HashMap<u32, Task>>>,
    next_id: u32,
    storage: S,
    reminder_thread: Option<thread::JoinHandle<()>>,
}
impl<S: TaskStorage> TaskManager<S> {
    pub async fn new(storage: S) -> Result<Self, S::Error> {
        let (tasks, next_id) = storage.load().await?;
        let tasks = Arc::new(Mutex::new(tasks));
        
        let manager = Self {
            tasks,
            next_id,
            storage,
            reminder_thread: None,
        };
        
        // 启动提醒线程
        manager.start_reminder_thread();
        
        Ok(manager)
    }
    
    // 启动任务提醒线程
    fn start_reminder_thread(&mut self) {
        let tasks = Arc::clone(&self.tasks);
        let (sender, receiver) = mpsc::channel();
        
        let handle = thread::spawn(move || {
            println!("任务提醒线程已启动");
            loop {
                // 检查到期任务
                let tasks = tasks.lock().unwrap();
                let now = SystemTime::now();
                
                for (id, task) in &*tasks {
                    if task.status == TaskStatus::Todo {
                        if let Ok(dur) = task.due_date.duration_since(now) {
                            if dur < Duration::from_secs(86400) { // 24小时内到期
                                sender.send((id.clone(), task.title.clone())).unwrap();
                            }
                        }
                    }
                }
                
                // 每秒检查一次
                thread::sleep(Duration::from_secs(1));
            }
        });
        
        self.reminder_thread = Some(handle);
    }
    
    // 添加任务
    pub fn add_task(&mut self, title: String, description: String, due_date: SystemTime) {
        let mut tasks = self.tasks.lock().unwrap();
        let task = Task::new(self.next_id, title, description, due_date);
        tasks.insert(self.next_id, task);
        println!("任务已添加,ID: {}", self.next_id);
        self.next_id += 1;
    }
    
    // 列出任务
    pub fn list_tasks(&self) {
        let tasks = self.tasks.lock().unwrap();
        if tasks.is_empty() {
            println!("暂无任务");
            return;
        }
        
        println!("ID\t状态\t标题\t\t截止日期");
        println!("----------------------------------------");
        
        for (_, task) in tasks {
            let status = match task.status {
                TaskStatus::Todo => "待办",
                TaskStatus::InProgress => "进行中",
                TaskStatus::Completed => "已完成",
            };
            let due_date = match task.due_date.duration_since(SystemTime::UNIX_EPOCH) {
                Ok(dur) => format!("{}", dur.as_secs()),
                Err(_) => "未知日期".to_string(),
            };
            println!("{}\t{}\t{}\t{}", task.id, status, task.title, due_date);
        }
    }
    
    // 更新任务状态
    pub fn update_task_status(&mut self, task_id: u32, status: TaskStatus) {
        let mut tasks = self.tasks.lock().unwrap();
        if let Some(task) = tasks.get_mut(&task_id) {
            task.update_status(status);
            println!("任务状态已更新");
        } else {
            println!("未找到 ID 为 {} 的任务", task_id);
        }
    }
    
    // 删除任务
    pub fn remove_task(&mut self, task_id: u32) -> Result<(), &'static str> {
        let mut tasks = self.tasks.lock().unwrap();
        if tasks.remove(&task_id).is_none() {
            Err("任务不存在")
        } else {
            Ok(())
        }
    }
    
    // 保存任务
    pub async fn save(&self) -> Result<(), S::Error> {
        let tasks = self.tasks.lock().unwrap();
        self.storage.save(tasks, self.next_id).await
    }
}

4. 整合所有优化并测试

更新 main.rs 以使用异步存储和 clap 命令行接口,并测试新功能。

rust 复制代码
// src/main.rs
mod task;
mod cli;
mod storage;
use cli::TaskManager;
use storage::AsyncFileStorage;
use task::TaskStatus;
use clap::Parser;
use std::path::PathBuf;
use std::time::SystemTime;
use tokio::runtime::Runtime;
// 命令行参数定义
#[derive(Parser, Debug)]
#[clap(author, version, about = "RustTask - 高效的命令行任务管理工具")]
enum Command {
    Add {
        title: String,
        description: String,
        #[clap(short, long, default_value = "today")]
        due: String,
    },
    List,
    Update {
        id: u32,
        status: String,
    },
    Delete {
        id: u32,
        #[clap(short, long, default_value = "false")]
        force: bool,
    },
}
fn main() {
    // 初始化Tokio运行时
    let runtime = Runtime::new().unwrap();
    
    // 解析命令行参数
    let command = Command::parse();
    let storage_path = PathBuf::from("tasks.json");
    let storage = AsyncFileStorage::new(storage_path.clone());
    
    // 运行异步任务管理器初始化
    let manager = runtime.block_on(TaskManager::new(storage));
    let mut manager = match manager {
        Ok(manager) => manager,
        Err(e) => {
            eprintln!("初始化任务管理器失败: {}", e);
            return;
        }
    };
    
    // 处理命令
    match command {
        Command::Add { title, description, due } => {
            let due_date = match due.as_str() {
                "today" => SystemTime::now() + std::time::Duration::from_secs(86400),
                _ => SystemTime::now() + std::time::Duration::from_secs(86400),
            };
            manager.add_task(title, description, due_date);
            runtime.block_on(manager.save()).unwrap();
        },
        Command::List => {
            manager.list_tasks();
        },
        Command::Update { id, status } => {
            let status = match status.as_str() {
                "todo" => TaskStatus::Todo,
                "in-progress" => TaskStatus::InProgress,
                "completed" => TaskStatus::Completed,
                _ => {
                    eprintln!("无效状态");
                    return;
                }
            };
            manager.update_task_status(id, status);
            runtime.block_on(manager.save()).unwrap();
        },
        Command::Delete { id, force } => {
            if !force {
                println!("确认删除任务 {}? (y/n)", id);
                let mut input = String::new();
                std::io::stdin().read_line(&mut input).unwrap();
                if input.trim() != "y" {
                    println!("取消删除");
                    return;
                }
            }
            if let Err(e) = manager.remove_task(id) {
                eprintln!("删除任务失败: {}", e);
            } else {
                println!("任务已删除");
                runtime.block_on(manager.save()).unwrap();
            }
        },
    }
}

实践作业

为 RustTask 添加网络同步功能,使用异步 HTTP 客户端实现任务数据的云端同步,具体要求:

  1. 添加sync子命令,用于同步任务数据
  1. 使用reqwest库实现异步 HTTP 请求
  1. 定义云端 API 接口,支持任务的增删改查
  1. 实现本地与云端任务数据的合并逻辑
  1. 处理网络错误与数据冲突
rust 复制代码
# Cargo.toml
[dependencies]
reqwest = { version = "0.11", features = ["json", "async-std"] }
async-std = { version = "1.12", features = ["runtime"] }
rust 复制代码
// src/storage.rs
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
use tokio::time::Duration;
// 云端任务数据结构
#[derive(Serialize, Deserialize, Debug)]
struct CloudTask {
    id: u32,
    title: String,
    description: String,
    due_date: u64,
    status: String,
}
// 云端存储实现
pub struct CloudStorage {
    base_url: String,
    client: Client,
}
impl CloudStorage {
    pub fn new(base_url: String) -> Result<Self, Box<dyn Error>> {
        let client = reqwest::Client::builder()
            .timeout(Duration::from_secs(10))
            .build()?;
        Ok(CloudStorage { base_url, client })
    }
}
impl TaskStorage for CloudStorage {
    type Error = Box<dyn Error>;
    
    async fn save(&self, tasks: &HashMap<u32, Task>, next_id: u32) -> Result<(), Self::Error> {
        // 转换为云端任务格式
        let cloud_tasks = tasks.values().map(|task| {
            CloudTask {
                id: task.id,
                title: task.title.clone(),
                description: task.description.clone(),
                due_date: task.due_date.duration_since(SystemTime::UNIX_EPOCH)?.as_secs(),
                status: match task.status {
                    TaskStatus::Todo => "todo".to_string(),
                    TaskStatus::InProgress => "in-progress".to_string(),
                    TaskStatus::Completed => "completed".to_string(),
                },
            }
        }).collect::<Vec<_>>();
        
        // 发送PUT请求同步任务
        self.client.put(format!("{}/tasks", self.base_url))
            .json(&cloud_tasks)
            .send()
            .await?
            .error_for_status()?;
        
        Ok(())
    }
    
    async fn load(&self) -> Result<(HashMap<u32, Task>, u32), Self::Error> {
        // 发送GET请求获取云端任务
        let cloud_tasks: Vec<CloudTask> = self.client.get(format!("{}/tasks", self.base_url))
            .send()
            .await?
            .error_for_status()?
            .json()
            .await?;
        
        // 转换为本地任务格式
        let mut tasks = HashMap::new();
        let mut next_id = 1;
        
        for cloud_task in cloud_tasks {
            let status = match cloud_task.status.as_str() {
                "todo" => TaskStatus::Todo,
                "in-progress" => TaskStatus::InProgress,
                "completed" => TaskStatus::Completed,
                _ => TaskStatus::Todo,
            };
            
            let due_date = SystemTime::UNIX_EPOCH + Duration::from_secs(cloud_task.due_date);
            tasks.insert(cloud_task.id, Task::new(
                cloud_task.id,
                cloud_task.title,
                cloud_task.description,
                due_date,
            ));
            
            if cloud_task.id >= next_id {
                next_id = cloud_task.id + 1;
            }
        }
        
        Ok((tasks, next_id))
    }
}

通过完成这个作业,你将综合运用异步编程、并发编程和网络编程等高级特性,进一步提升 Rust 项目开发能力,理解如何构建具有网络功能的复杂应用程序。

相关推荐
小高0074 分钟前
📈前端图片压缩实战:体积直降 80%,LCP 提升 2 倍
前端·javascript·面试
OEC小胖胖7 分钟前
【React Hooks】封装的艺术:如何编写高质量的 React 自-定义 Hooks
前端·react.js·前端框架·web
BillKu15 分钟前
vue3+element-plus 输入框el-input设置背景颜色和字体颜色,样式效果等同于不可编辑的效果
前端·javascript·vue.js
惊悚的毛毛虫19 分钟前
掘金免广告?不想看理财交流圈?不想看exp+8?
前端
springfe010125 分钟前
vue3组件 - 大文件上传
前端·vue.js
再学一点就睡33 分钟前
Vite 工作原理(简易版)—— 从代码看核心逻辑
前端·vite
好好好明天会更好1 小时前
uniapp项目中小程序的生命周期
前端·vue.js
CF14年老兵1 小时前
「Vue 3 + View Transition 实现炫酷圆形缩放换肤动画」
前端·css·trae
小璞1 小时前
05_CursorRules_代码审查篇_Rule_code-review
前端