
📑 目录
- 前言:为什么要做项目?
- [项目介绍:待办事项 CLI](#项目介绍:待办事项 CLI)
- 第一步:基础语法
- 第二步:结构体与方法
- 第三步:错误处理
- 第四步:文件读写
- 第五步:命令行参数
- 完整代码与总结
前言:为什么要做项目?💡
学编程最快的方式:边做边学!
本文通过一个待办事项命令行工具,串联 Rust 所有核心基础:
- ✅ 变量与数据类型
- ✅ 结构体与方法
- ✅ 所有权与借用
- ✅ 错误处理
- ✅ 文件 I/O
- ✅ 模式匹配
项目效果:
bash
$ todo add "学习 Rust"
✓ 任务已添加
$ todo list
1. [ ] 学习 Rust
$ todo done 1
✓ 任务已完成
项目介绍:Todo CLI 📝
功能设计
| 命令 | 功能 |
|---|---|
todo add <task> |
添加任务 |
todo list |
列出所有任务 |
todo done <id> |
完成任务 |
todo remove <id> |
删除任务 |
技术栈
- 标准库:
std::fs,std::env - JSON 序列化:
serde - 命令行解析:手写(学习基础)
第一步:基础语法 🎯
1.1 创建项目
bash
cargo new todo_cli
cd todo_cli
1.2 定义数据结构
rust
// src/main.rs
// 任务状态
#[derive(Debug, Clone)]
enum Status {
Pending, // 待完成
Done, // 已完成
}
// 任务结构
#[derive(Debug, Clone)]
struct Task {
id: usize,
title: String,
status: Status,
}
// 任务管理器
struct TodoList {
tasks: Vec<Task>,
next_id: usize,
}
知识点:
enum:枚举类型struct:结构体derive:自动派生 traitVec<T>:动态数组
第二步:实现核心功能 ⚙️
2.1 添加任务
rust
impl TodoList {
// 创建新列表
fn new() -> Self {
TodoList {
tasks: Vec::new(),
next_id: 1,
}
}
// 添加任务
fn add(&mut self, title: String) {
let task = Task {
id: self.next_id,
title,
status: Status::Pending,
};
self.tasks.push(task);
self.next_id += 1;
println!("✓ 任务已添加");
}
}
知识点:
impl:实现方法&mut self:可变借用Self:类型别名
2.2 列出任务
rust
impl TodoList {
fn list(&self) {
if self.tasks.is_empty() {
println!("暂无任务");
return;
}
for task in &self.tasks {
let checkbox = match task.status {
Status::Pending => "[ ]",
Status::Done => "[✓]",
};
println!("{}. {} {}", task.id, checkbox, task.title);
}
}
}
知识点:
&self:不可变借用match:模式匹配for循环
2.3 完成任务
rust
impl TodoList {
fn done(&mut self, id: usize) -> Result<(), String> {
let task = self.tasks
.iter_mut()
.find(|t| t.id == id)
.ok_or("任务不存在")?;
task.status = Status::Done;
println!("✓ 任务已完成");
Ok(())
}
}
知识点:
Result<T, E>:错误处理iter_mut():可变迭代器find():查找元素?:错误传播
2.4 删除任务
rust
impl TodoList {
fn remove(&mut self, id: usize) -> Result<(), String> {
let index = self.tasks
.iter()
.position(|t| t.id == id)
.ok_or("任务不存在")?;
self.tasks.remove(index);
println!("✓ 任务已删除");
Ok(())
}
}
第三步:文件持久化 💾
3.1 添加依赖
toml
# Cargo.toml
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
3.2 修改数据结构
rust
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
enum Status {
Pending,
Done,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Task {
id: usize,
title: String,
status: Status,
}
3.3 保存到文件
rust
use std::fs;
use std::io::{self, Write};
impl TodoList {
fn save(&self) -> io::Result<()> {
let json = serde_json::to_string_pretty(&self.tasks)?;
fs::write("todos.json", json)?;
Ok(())
}
fn load() -> io::Result<Self> {
match fs::read_to_string("todos.json") {
Ok(content) => {
let tasks: Vec<Task> = serde_json::from_str(&content)?;
let next_id = tasks.iter()
.map(|t| t.id)
.max()
.unwrap_or(0) + 1;
Ok(TodoList { tasks, next_id })
}
Err(_) => Ok(TodoList::new()),
}
}
}
知识点:
fs::write():写文件fs::read_to_string():读文件io::Result<T>:I/O 错误处理serde_json:JSON 序列化
第四步:命令行解析 🖥️
4.1 解析参数
rust
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
print_help();
return;
}
let mut todo_list = TodoList::load()
.expect("加载任务失败");
let command = &args[1];
match command.as_str() {
"add" => {
if args.len() < 3 {
println!("用法: todo add <任务>");
return;
}
let title = args[2..].join(" ");
todo_list.add(title);
}
"list" => {
todo_list.list();
}
"done" => {
if args.len() < 3 {
println!("用法: todo done <id>");
return;
}
let id: usize = args[2].parse()
.expect("ID 必须是数字");
todo_list.done(id).ok();
}
"remove" => {
if args.len() < 3 {
println!("用法: todo remove <id>");
return;
}
let id: usize = args[2].parse()
.expect("ID 必须是数字");
todo_list.remove(id).ok();
}
_ => print_help(),
}
todo_list.save().expect("保存失败");
}
fn print_help() {
println!("Todo CLI - 待办事项管理");
println!("\n用法:");
println!(" todo add <任务> 添加任务");
println!(" todo list 列出任务");
println!(" todo done <id> 完成任务");
println!(" todo remove <id> 删除任务");
}
知识点:
env::args():获取命令行参数parse():字符串解析expect():简化错误处理as_str():字符串切片
完整代码 📦
src/main.rs
rust
use serde::{Serialize, Deserialize};
use std::fs;
use std::io;
use std::env;
#[derive(Debug, Clone, Serialize, Deserialize)]
enum Status {
Pending,
Done,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Task {
id: usize,
title: String,
status: Status,
}
struct TodoList {
tasks: Vec<Task>,
next_id: usize,
}
impl TodoList {
fn new() -> Self {
TodoList {
tasks: Vec::new(),
next_id: 1,
}
}
fn add(&mut self, title: String) {
let task = Task {
id: self.next_id,
title,
status: Status::Pending,
};
self.tasks.push(task);
self.next_id += 1;
println!("✓ 任务已添加");
}
fn list(&self) {
if self.tasks.is_empty() {
println!("暂无任务");
return;
}
for task in &self.tasks {
let checkbox = match task.status {
Status::Pending => "[ ]",
Status::Done => "[✓]",
};
println!("{}. {} {}", task.id, checkbox, task.title);
}
}
fn done(&mut self, id: usize) -> Result<(), String> {
let task = self.tasks
.iter_mut()
.find(|t| t.id == id)
.ok_or("任务不存在")?;
task.status = Status::Done;
println!("✓ 任务已完成");
Ok(())
}
fn remove(&mut self, id: usize) -> Result<(), String> {
let index = self.tasks
.iter()
.position(|t| t.id == id)
.ok_or("任务不存在")?;
self.tasks.remove(index);
println!("✓ 任务已删除");
Ok(())
}
fn save(&self) -> io::Result<()> {
let json = serde_json::to_string_pretty(&self.tasks)?;
fs::write("todos.json", json)?;
Ok(())
}
fn load() -> io::Result<Self> {
match fs::read_to_string("todos.json") {
Ok(content) => {
let tasks: Vec<Task> = serde_json::from_str(&content)?;
let next_id = tasks.iter()
.map(|t| t.id)
.max()
.unwrap_or(0) + 1;
Ok(TodoList { tasks, next_id })
}
Err(_) => Ok(TodoList::new()),
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
print_help();
return;
}
let mut todo_list = TodoList::load()
.expect("加载任务失败");
let command = &args[1];
match command.as_str() {
"add" => {
if args.len() < 3 {
println!("用法: todo add <任务>");
return;
}
let title = args[2..].join(" ");
todo_list.add(title);
}
"list" => {
todo_list.list();
}
"done" => {
if args.len() < 3 {
println!("用法: todo done <id>");
return;
}
let id: usize = args[2].parse()
.expect("ID 必须是数字");
todo_list.done(id).ok();
}
"remove" => {
if args.len() < 3 {
println!("用法: todo remove <id>");
return;
}
let id: usize = args[2].parse()
.expect("ID 必须是数字");
todo_list.remove(id).ok();
}
_ => print_help(),
}
todo_list.save().expect("保存失败");
}
fn print_help() {
println!("Todo CLI - 待办事项管理");
println!("\n用法:");
println!(" todo add <任务> 添加任务");
println!(" todo list 列出任务");
println!(" todo done <id> 完成任务");
println!(" todo remove <id> 删除任务");
}
运行与测试 ✅
bash
# 编译
cargo build --release
# 运行
cargo run -- add "学习 Rust"
cargo run -- list
cargo run -- done 1
cargo run -- remove 1
知识点总结 📚
学到了什么?
| 概念 | 应用位置 |
|---|---|
| 结构体 | Task, TodoList |
| 枚举 | Status |
| 方法 | add, list, done, remove |
| 所有权 | 参数传递、返回值 |
| 借用 | &self, &mut self |
| 错误处理 | Result, ? 运算符 |
| 文件 I/O | save, load |
| 序列化 | serde, serde_json |
| 模式匹配 | match 命令解析 |
| 迭代器 | find, position |
进阶方向 🚀
功能扩展:
- 添加截止日期
- 任务优先级
- 任务分类
- 搜索功能
技术优化:
- 使用
clap解析命令行 - 添加单元测试
- 使用数据库(SQLite)
- 彩色终端输出(colored)