实战项目清单
完成理论学习后, 通过实际项目巩固知识
每个项目都对应多个核心章节的知识点
项目 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>;进阶功能
- 并行搜索: 使用 Rayon 并行处理多文件
- 正则支持: 集成
regexcrate - 流式输出: 文件边读边输出,不全部加载到内存
- 性能对比: 和系统 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!()
}
}进阶功能
- 分片下载: 对支持 Range 请求的服务器使用多线程分片
- 进度持久化: 保存下载进度到文件,支持中断恢复
- 速度限制: 实现令牌桶算法限制下载速度
- 哈希校验: 下载完成后验证 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" }
}进阶功能
- 并行解析: 使用 Rayon 并行处理多个文件
- 内存效率: 使用流式处理, 处理 GB 级日志文件
- 实时监控: 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 的替代品 |
| cargo | E-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 个项目, 理论学习和实践相互促进。
🦀 实践是最好的老师, 开始构建吧!