实战项目清单

完成理论学习后, 通过实际项目巩固知识

每个项目都对应多个核心章节的知识点


项目 1: 命令行 grep 工具

对应章节: 01, 02, 03
预估时间: 3-5 天
难度: ⭐⭐

功能需求

minigrep [选项] <pattern> <文件...>

选项:
  -i, --ignore-case     忽略大小写
  -n, --line-number     显示行号
  -c, --count           只显示匹配行数
  -r, --recursive       递归搜索目录
  -v, --invert          反向匹配
  --color               彩色输出

示例:
  minigrep "fn main" src/main.rs
  minigrep -r -i "todo" src/
  minigrep -n "error" logs/app.log

技术架构

src/
├── main.rs         # CLI 解析 + 入口
├── lib.rs          # 库接口
├── search.rs       # 搜索逻辑
├── output.rs       # 输出格式化
└── error.rs        # 错误类型

关键实现

// CLI 使用 clap
use clap::Parser;
 
#[derive(Parser)]
#[command(name = "minigrep")]
#[command(about = "A simple grep implementation")]
struct Args {
    #[arg(help = "Pattern to search")]
    pattern: String,
    
    #[arg(help = "Files to search")]
    files: Vec<PathBuf>,
    
    #[arg(short = 'i', long)]
    ignore_case: bool,
    
    #[arg(short = 'n', long)]
    line_number: bool,
    
    #[arg(short = 'r', long)]
    recursive: bool,
}
 
// 搜索结果
#[derive(Debug)]
pub struct SearchResult {
    pub file: PathBuf,
    pub line_number: usize,
    pub line: String,
    pub matches: Vec<(usize, usize)>,  // (start, end) 字节偏移
}
 
// 搜索函数签名
pub fn search(
    pattern: &str,
    content: &str,
    options: &SearchOptions,
) -> Vec<SearchResult>;

进阶功能

  1. 并行搜索: 使用 Rayon 并行处理多文件
  2. 正则支持: 集成 regex crate
  3. 流式输出: 文件边读边输出,不全部加载到内存
  4. 性能对比: 和系统 grep 进行基准测试

评估标准

  • 基本搜索功能正确
  • 错误处理完善 (文件不存在, 权限不足等)
  • 单元测试覆盖率 > 80%
  • 处理 1GB 文件不 OOM
  • 性能在系统 grep 的 2 倍以内

项目 2: 并发文件下载器

对应章节: 05, 09
预估时间: 4-6 天
难度: ⭐⭐⭐

功能需求

downloader [选项] <URL...>

选项:
  -c, --concurrent <N>   并发数 (默认 4)
  -o, --output <dir>     输出目录 (默认当前目录)
  --retry <N>            失败重试次数
  --timeout <secs>       超时时间

特性:
  - 显示下载进度条
  - 支持断点续传
  - 多线程分片下载大文件

技术架构

// 核心数据流
URL列表
  ↓ (解析)
DownloadTask { url, output_path, range? }
  ↓ (并发执行)
HTTP请求 (reqwest)
  ↓ (流式写入)
临时文件
  ↓ (合并)
最终文件

关键代码结构

use tokio::sync::Semaphore;
use tokio_stream::StreamExt;
use reqwest::Client;
use indicatif::{MultiProgress, ProgressBar};
 
pub struct Downloader {
    client: Client,
    semaphore: Arc<Semaphore>,
    progress: MultiProgress,
    options: DownloadOptions,
}
 
#[derive(Debug, Clone)]
pub struct DownloadOptions {
    pub concurrent: usize,
    pub retry: usize,
    pub timeout: Duration,
    pub output_dir: PathBuf,
}
 
impl Downloader {
    pub async fn download_all(&self, urls: Vec<String>) -> Vec<DownloadResult> {
        let futures: Vec<_> = urls.into_iter()
            .map(|url| self.download_one(url))
            .collect();
        
        futures::future::join_all(futures).await
    }
    
    async fn download_one(&self, url: String) -> DownloadResult {
        let _permit = self.semaphore.acquire().await.unwrap();
        
        // 实现重试逻辑
        for attempt in 0..=self.options.retry {
            match self.do_download(&url).await {
                Ok(path) => return DownloadResult::Success(path),
                Err(e) if attempt < self.options.retry => {
                    eprintln!("Retry {}/{}: {}", attempt + 1, self.options.retry, e);
                    tokio::time::sleep(Duration::from_secs(1)).await;
                }
                Err(e) => return DownloadResult::Failed(e.to_string()),
            }
        }
        unreachable!()
    }
}

进阶功能

  1. 分片下载: 对支持 Range 请求的服务器使用多线程分片
  2. 进度持久化: 保存下载进度到文件,支持中断恢复
  3. 速度限制: 实现令牌桶算法限制下载速度
  4. 哈希校验: 下载完成后验证 MD5/SHA256

评估标准

  • 并发下载多个文件正确
  • 进度条显示准确
  • 网络错误能正确重试
  • 磁盘写入使用 Buffer (避免频繁 syscall)
  • 取消下载时清理临时文件

项目 3: 结构化日志解析器

对应章节: 04, 06, 08
预估时间: 5-7 天
难度: ⭐⭐⭐

功能需求

logparser [选项] <log文件>

支持格式:
  - Nginx access log
  - JSON Lines
  - 用户自定义正则

分析功能:
  - Top N 请求路径
  - 请求状态码分布
  - 响应时间分位数 (p50, p95, p99)
  - 时间段内的 QPS
  - IP 访问频次统计

输出格式:
  - 终端表格
  - JSON
  - CSV

关键实现

// 使用 Trait 抽象解析器
trait LogParser: Send + Sync {
    fn parse_line(&self, line: &str) -> Option<LogEntry>;
    fn name(&self) -> &str;
}
 
#[derive(Debug, Clone)]
struct LogEntry {
    timestamp: chrono::DateTime<chrono::Utc>,
    level: Option<LogLevel>,
    message: String,
    fields: std::collections::HashMap<String, serde_json::Value>,
}
 
// 流式处理 + 聚合
struct LogAnalyzer {
    parsers: Vec<Box<dyn LogParser>>,
    aggregators: Vec<Box<dyn Aggregator>>,
}
 
trait Aggregator: Send + Sync {
    fn feed(&mut self, entry: &LogEntry);
    fn report(&self) -> serde_json::Value;
    fn name(&self) -> &str;
}
 
// 示例聚合器: 响应时间统计
struct LatencyStats {
    samples: Vec<f64>,
}
 
impl Aggregator for LatencyStats {
    fn feed(&mut self, entry: &LogEntry) {
        if let Some(latency) = entry.fields.get("duration") {
            if let Some(v) = latency.as_f64() {
                self.samples.push(v);
            }
        }
    }
    
    fn report(&self) -> serde_json::Value {
        let mut sorted = self.samples.clone();
        sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
        
        let percentile = |p: f64| -> f64 {
            let idx = ((sorted.len() as f64 * p) as usize).min(sorted.len() - 1);
            sorted[idx]
        };
        
        serde_json::json!({
            "count": sorted.len(),
            "p50": percentile(0.5),
            "p95": percentile(0.95),
            "p99": percentile(0.99),
            "max": sorted.last().copied().unwrap_or(0.0),
        })
    }
    
    fn name(&self) -> &str { "latency_stats" }
}

进阶功能

  1. 并行解析: 使用 Rayon 并行处理多个文件
  2. 内存效率: 使用流式处理, 处理 GB 级日志文件
  3. 实时监控: tail -f 功能, 实时输出统计

评估标准

  • 正确解析主流日志格式
  • 处理 1GB 日志文件内存 < 100MB
  • 统计结果的准确性
  • 友好的错误提示 (格式不匹配等)

项目 4: 迷你 Key-Value 存储

对应章节: 06, 07, 09
预估时间: 7-10 天
难度: ⭐⭐⭐⭐

功能需求

类 Redis 的 K-V 存储服务

支持命令:
  SET key value [EX seconds]
  GET key
  DEL key [key ...]
  KEYS pattern
  EXPIRE key seconds
  TTL key
  INCR key
  LPUSH/LPOP key value
  HSET/HGET key field value

特性:
  - 基于 TCP 的文本协议 (RESP 简化版)
  - 数据持久化 (AOF 日志)
  - LRU 淘汰策略
  - 异步 IO

技术架构

客户端 TCP 连接
    ↓
协议解析 (自定义 RESP 解析器)
    ↓
命令分发器
    ↓
存储引擎 (HashMap + LRU + TTL)
    ↓
持久化 (AOF 文件)

关键代码

use tokio::net::TcpListener;
use std::sync::Arc;
use tokio::sync::RwLock;
 
// 存储引擎
type Db = Arc<RwLock<Storage>>;
 
pub struct Storage {
    data: std::collections::HashMap<String, Value>,
    expiry: std::collections::BTreeMap<std::time::Instant, String>,
}
 
#[derive(Debug, Clone)]
pub enum Value {
    String(String),
    Integer(i64),
    List(std::collections::VecDeque<String>),
    Hash(std::collections::HashMap<String, String>),
}
 
// 命令
#[derive(Debug)]
pub enum Command {
    Get { key: String },
    Set { key: String, value: String, expiry: Option<Duration> },
    Del { keys: Vec<String> },
    // ...
}
 
// 主循环
#[tokio::main]
async fn main() {
    let db: Db = Arc::new(RwLock::new(Storage::new()));
    let listener = TcpListener::bind("127.0.0.1:6379").await.unwrap();
    
    loop {
        let (socket, _) = listener.accept().await.unwrap();
        let db = Arc::clone(&db);
        
        tokio::spawn(async move {
            handle_client(socket, db).await;
        });
    }
}

评估标准

  • 基本命令正确执行
  • 并发安全 (多客户端同时操作)
  • TTL 过期正确处理
  • AOF 持久化可以恢复数据
  • 用 redis-cli 测试基本兼容

项目 5: 阅读并贡献开源项目

对应章节: 10
预估时间: 持续进行
难度: ⭐⭐⭐⭐⭐

推荐入门项目

项目Good First Issues说明
ripgrep~5-10 个高质量代码, 维护活跃
bat~10 个cat 的替代品, 代码清晰
fd~5 个find 的替代品
cargoE-easy 标签Rust 工具链

贡献步骤

第一周: 熟悉项目
  ├── 阅读 README 和 CONTRIBUTING.md
  ├── 构建并运行所有测试
  ├── 运行项目并体验功能
  └── 浏览 Good First Issues

第二周: 找到目标
  ├── 选择一个感兴趣的 Issue
  ├── 在 Issue 下留言 "I'd like to work on this"
  └── 开始理解相关代码

第三周: 实现
  ├── 写代码 + 测试
  ├── 确保 cargo test / clippy / fmt 全过
  └── 提交 PR

持续: 保持参与
  ├── 回应 Code Review 意见
  ├── 继续找下一个 Issue
  └── 帮助其他贡献者

评估标准

  • 至少提交 1 个 PR
  • PR 包含测试
  • 通过 CI 检查
  • 获得 maintainer 的正面反馈

总结: 项目选择建议

阶段推荐项目重点
完成 1-3 章项目 1: minigrep所有权, 错误处理
完成 1-5 章项目 3: 日志解析Trait, 并发
完成 1-9 章项目 2: 下载器异步, 并发
完成全部章节项目 4: KV存储综合
任何时候项目 5: 开源贡献真实环境

建议同时进行 1-2 个项目, 理论学习和实践相互促进。


🦀 实践是最好的老师, 开始构建吧!