从 “不会” 到 “会写”:Rust 入门基础实战,用一个小项目串完所有核心基础

📑 目录


前言:为什么要做项目?💡

学编程最快的方式:边做边学

本文通过一个待办事项命令行工具,串联 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:自动派生 trait
  • Vec<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)

相关推荐
筱砚.3 小时前
【STL——set与multiset容器】
开发语言·c++·stl
Fanfffff7203 小时前
从TSX到JS:深入解析npm run build背后的完整构建流程
开发语言·javascript·npm
程序员爱钓鱼3 小时前
Python编程实战 - 函数与模块化编程 - 导入与使用模块
后端·python·ipython
Elias不吃糖3 小时前
C++ 中的浅拷贝与深拷贝:概念、规则、示例与最佳实践(笔记)
开发语言·c++·浅拷贝·深拷贝
LEEBELOVED3 小时前
R语言高效数据处理-3个自定义函数笔记
开发语言·笔记·r语言
程序员爱钓鱼3 小时前
Python编程实战 - 函数与模块化编程 - 匿名函数(lambda)
后端·python·ipython
朝新_3 小时前
【SpringMVC】SpringMVC 请求与响应全解析:从 Cookie/Session 到状态码、Header 配置
java·开发语言·笔记·springmvc·javaee
Moment3 小时前
记录一次修改 PNPM 版本,部署 NextJs 服务时导致服务器崩溃的问题 😡😡😡
前端·javascript·后端
2501_938782093 小时前
从实例到单例:Objective-C 单例类的线程安全实现方案
开发语言·macos·objective-c