首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >46-Rust 教程 - 完整项目

46-Rust 教程 - 完整项目

作者头像
LarryLan
发布2026-06-30 14:59:33
发布2026-06-30 14:59:33
1070
举报

完整项目

从零到一:构建一个完整的 Rust 应用

🎬 引入

学了这么多 Rust 知识,是时候做个完整的项目了!

今天咱们从零开始,构建一个任务管理 CLI 工具,名字叫 taskmaster

功能清单:

  • ✅ 添加任务
  • ✅ 列出任务
  • ✅ 完成任务
  • ✅ 删除任务
  • ✅ 数据持久化(JSON 文件)
  • ✅ 彩色输出
  • ✅ 错误处理

用到的知识:

  • 结构体、枚举
  • 错误处理(Result、thiserror)
  • 文件 I/O
  • 序列化(serde)
  • CLI 参数解析(clap)
  • 测试

📌 项目结构

代码语言:javascript
复制
taskmaster/
├── Cargo.toml
├── src/
│   ├── main.rs          # 入口
│   ├── lib.rs           # 库代码
│   ├── task.rs          # 任务定义
│   ├── storage.rs       # 数据存储
│   └── commands.rs      # 命令处理
├── tests/
│   └── integration.rs   # 集成测试
└── README.md

💻 实战步骤

步骤 1:创建项目

代码语言:javascript
复制
cargo new taskmaster
cd taskmaster

# 添加依赖
cargo add clap --features derive
cargo add serde --features derive
cargo add serde_json
cargo add thiserror
cargo add colored
cargo add dirs

步骤 2:定义任务结构

代码语言:javascript
复制
// src/task.rs
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Task {
    pub id: u32,
    pub title: String,
    pub description: Option<String>,
    pub completed: bool,
    pub created_at: DateTime<Utc>,
    pub completed_at: Option<DateTime<Utc>>,
}

impl Task {
    pub fn new(id: u32, title: String, description: Option<String>) -> Self {
        Task {
            id,
            title,
            description,
            completed: false,
            created_at: Utc::now(),
            completed_at: None,
        }
    }
    
    pub fn complete(&mut self) {
        self.completed = true;
        self.completed_at = Some(Utc::now());
    }
}

#[derive(Debug, Clone)]
pub enum Priority {
    Low,
    Medium,
    High,
}

步骤 3:实现数据存储

代码语言:javascript
复制
// src/storage.rs
use std::fs;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::task::Task;

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct TaskStore {
    tasks: Vec<Task>,
    next_id: u32,
}

impl TaskStore {
    pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
        let path = Self::get_path()?;
        
        if !path.exists() {
            return Ok(TaskStore::default());
        }
        
        let content = fs::read_to_string(path)?;
        let store: TaskStore = serde_json::from_str(&content)?;
        Ok(store)
    }
    
    pub fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
        let path = Self::get_path()?;
        let content = serde_json::to_string_pretty(self)?;
        fs::write(path, content)?;
        Ok(())
    }
    
    pub fn add_task(&mut self, title: String, description: Option<String>) -> &Task {
        let task = Task::new(self.next_id, title, description);
        self.next_id += ;
        self.tasks.push(task);
        self.tasks.last().unwrap()
    }
    
    pub fn list_tasks(&self) -> &[Task] {
        &self.tasks
    }
    
    pub fn complete_task(&mut self, id: u32) -> Option<&Task> {
        self.tasks.iter_mut()
            .find(|t| t.id == id)
            .map(|task| {
                task.complete();
                task
            })
    }
    
    pub fn delete_task(&mut self, id: u32) -> Option<Task> {
        if let Some(pos) = self.tasks.iter().position(|t| t.id == id) {
            Some(self.tasks.remove(pos))
        } else {
            None
        }
    }
    
    fn get_path() -> Result<PathBuf, Box<dyn std::error::Error>> {
        let config_dir = dirs::config_dir()
            .ok_or("无法找到配置目录")?;
        Ok(config_dir.join("taskmaster").join("tasks.json"))
    }
}

步骤 4:实现命令处理

代码语言:javascript
复制
// src/commands.rs
use colored::Colorize;
use crate::storage::TaskStore;

pub fn add(store: &mut TaskStore, title: String, description: Option<String>) {
    let task = store.add_task(title, description);
    println!("✅ 已添加任务 #{}", task.id.to_string().green());
}

pub fn list(store: &TaskStore) {
    let tasks = store.list_tasks();
    
    if tasks.is_empty() {
        println!("{}", "暂无任务".yellow());
        return;
    }
    
    println!("\n{}", "📋 任务列表".bold());
    println!("{}", "=".repeat());
    
    for task in tasks {
        let status = if task.completed {
            "✓".green()
        } else {
            "○".yellow()
        };
        
        println!("{} #{} {}", status, task.id.to_string().bold(), task.title);
        
        if let Some(desc) = &task.description {
            println!("   {}", desc.dimmed());
        }
        
        println!("   创建时间:{}", task.created_at.format("%Y-%m-%d %H:%M"));
        
        if task.completed {
            println!("   {}", "✓ 已完成".green());
        }
        
        println!();
    }
}

pub fn complete(store: &mut TaskStore, id: u32) {
    match store.complete_task(id) {
        Some(task) => println!("✅ 任务 #{} 已完成!", task.id.to_string().green()),
        None => println!("{}", format!("❌ 未找到任务 #{}", id).red()),
    }
}

pub fn delete(store: &mut TaskStore, id: u32) {
    match store.delete_task(id) {
        Some(task) => println!("✅ 已删除任务 #{}: {}", task.id.to_string().green(), task.title),
        None => println!("{}", format!("❌ 未找到任务 #{}", id).red()),
    }
}

步骤 5:CLI 参数解析

代码语言:javascript
复制
// src/main.rs
use clap::{Parser, Subcommand};
use taskmaster::{storage::TaskStore, commands};

#[derive(Parser)]
#[command(name = "taskmaster")]
#[command(about = "一个强大的任务管理工具", long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// 添加新任务
    Add {
        /// 任务标题
        #[arg(short, long)]
        title: String,
        
        /// 任务描述
        #[arg(short, long)]
        description: Option<String>,
    },
    
    /// 列出所有任务
    List,
    
    /// 完成任务
    Complete {
        /// 任务 ID
        id: u32,
    },
    
    /// 删除任务
    Delete {
        /// 任务 ID
        id: u32,
    },
}

fn main() {
    let cli = Cli::parse();
    
    // 加载存储
    let mut store = match TaskStore::load() {
        Ok(store) => store,
        Err(e) => {
            eprintln!("❌ 加载数据失败:{}", e);
            std::process::exit();
        }
    };
    
    // 执行命令
    match cli.command {
        Commands::Add { title, description } => {
            commands::add(&mut store, title, description);
        }
        Commands::List => {
            commands::list(&store);
        }
        Commands::Complete { id } => {
            commands::complete(&mut store, id);
        }
        Commands::Delete { id } => {
            commands::delete(&mut store, id);
        }
    }
    
    // 保存数据
    if let Err(e) = store.save() {
        eprintln!("❌ 保存数据失败:{}", e);
        std::process::exit();
    }
}

步骤 6:编写测试

代码语言:javascript
复制
// tests/integration.rs
use taskmaster::storage::TaskStore;

#[test]
fn test_add_task() {
    let mut store = TaskStore::default();
    let task = store.add_task("测试任务".to_string(), Some("描述".to_string()));
    
    assert_eq!(task.id, );
    assert_eq!(task.title, "测试任务");
    assert_eq!(store.list_tasks().len(), );
}

#[test]
fn test_complete_task() {
    let mut store = TaskStore::default();
    store.add_task("任务 1".to_string(), None);
    
    store.complete_task();
    
    let task = &store.list_tasks()[];
    assert!(task.completed);
    assert!(task.completed_at.is_some());
}

#[test]
fn test_delete_task() {
    let mut store = TaskStore::default();
    store.add_task("任务 1".to_string(), None);
    store.add_task("任务 2".to_string(), None);
    
    store.delete_task();
    
    assert_eq!(store.list_tasks().len(), );
    assert_eq!(store.list_tasks()[].id, );
}

#[test]
fn test_persistence() {
    let mut store = TaskStore::default();
    store.add_task("持久化测试".to_string(), None);
    
    // 保存到临时文件
    // ...
}

步骤 7:编写文档

代码语言:javascript
复制
# Taskmaster

一个用 Rust 编写的命令行任务管理工具。

## 安装

```bash
cargo install --git https://github.com/yourusername/taskmaster

使用

添加任务

代码语言:javascript
复制
taskmaster add --title "学习 Rust" --description "完成教程"

列出任务

代码语言:javascript
复制
taskmaster list

完成任务

代码语言:javascript
复制
taskmaster complete 1

删除任务

代码语言:javascript
复制
taskmaster delete 1

开发

代码语言:javascript
复制
# 运行
cargo run -- list

# 测试
cargo test

# 构建 release
cargo build --release
代码语言:javascript
复制

## 🐛 常见问题

### 问题 1:依赖冲突

```bash
# 更新依赖
cargo update

# 检查依赖树
cargo tree

问题 2:跨平台路径

代码语言:javascript
复制
// ❌ 硬编码路径
let path = "C:\\Users\\data.json";

// ✅ 用跨平台库
use dirs;
let path = dirs::config_dir().unwrap().join("taskmaster");

问题 3:错误处理不统一

代码语言:javascript
复制
// ❌ 到处 unwrap
let file = fs::File::open(path).unwrap();

// ✅ 统一错误类型
use thiserror::Error;

#[derive(Error, Debug)]
pub enum TaskError {
    #[error("IO 错误:{0}")]
    Io(#[from] std::io::Error),
    
    #[error("JSON 错误:{0}")]
    Json(#[from] serde_json::Error),
}

🎯 扩展方向

这个项目可以继续扩展:

  1. 任务优先级:高/中/低优先级
  2. 截止日期:设置和提醒
  3. 标签系统:给任务打标签
  4. 搜索功能:按关键词搜索
  5. 导出功能:导出为 CSV/Markdown
  6. 同步功能:多设备同步
  7. TUI 界面:用 ratatui 做终端 UI

🧠 思维导图

46-完整项目
46-完整项目

📝 小结

金句回顾:

  1. 项目结构要清晰——每个模块职责单一
  2. 错误处理要统一——用 thiserror 定义错误类型
  3. 数据持久化——JSON 文件简单好用
  4. 测试要写——单元测试 + 集成测试
  5. 文档不能少——README 和使用说明

系列完结!

恭喜你完成了 Rust 教程系列 50 篇!从入门到实战,你已经掌握了:

  • ✅ 基础语法(所有权、借用、生命周期)
  • ✅ 核心概念(结构体、枚举、模式匹配)
  • ✅ 类型系统(泛型、Trait、闭包)
  • ✅ 内存管理(智能指针、引用计数)
  • ✅ 并发编程(线程、消息传递、异步)
  • ✅ 高级主题(宏、unsafe、FFI)
  • ✅ 实战项目(CLI、Web、WASM)

接下来:

  1. 继续写公众号文章,分享你的学习心得
  2. 用 Rust 做点有趣的项目
  3. 参与开源项目,贡献代码
  4. 帮助其他人学习 Rust

Rust 的学习之旅没有终点,但你已经有了坚实的开始。加油!🦀

🔗 参考资料

  • Rust 官方文档
  • Rust By Example
  • Awesome Rust
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-06-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Larry的Hub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 完整项目
    • 🎬 引入
    • 📌 项目结构
    • 💻 实战步骤
      • 步骤 1:创建项目
      • 步骤 2:定义任务结构
      • 步骤 3:实现数据存储
      • 步骤 4:实现命令处理
      • 步骤 5:CLI 参数解析
      • 步骤 6:编写测试
      • 步骤 7:编写文档
    • 使用
      • 添加任务
      • 列出任务
      • 完成任务
      • 删除任务
    • 开发
      • 问题 2:跨平台路径
      • 问题 3:错误处理不统一
    • 🎯 扩展方向
    • 🧠 思维导图
    • 📝 小结
    • 🔗 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档