第三章:命令行交互与流程控制

第三章:命令行交互与流程控制

教学目标

  • 掌握 Rust 流程控制语句(if-else、循环)的使用方法
  • 理解 IO 操作与用户交互的实现方式
  • 掌握 HashMap 集合类型的基本操作
  • 实现任务管理系统的基本交互逻辑

核心知识点

1. 流程控制

if-else 条件表达式

Rust 的 if-else 语句用于根据条件执行不同的代码块,条件必须是布尔类型,且不需要用括号包裹条件表达式。if 表达式可以作为值返回,这是 Rust 的特色之一。

rust 复制代码
fn main() {
    let x = 10;
    let y = 20;
    
    // 基本if-else结构
    if x > y {
        println!("x大于y");
    } else if x < y {
        println!("x小于y");
    } else {
        println!("x等于y");
    }
    
    // if表达式作为值
    let result = if x > y {
        "x大于y"
    } else {
        "x不大于y"
    };
    
    println!("结果: {}", result);
}
loop/while 循环与 break/continue

Rust 提供了三种循环类型:loop(无限循环)、while(条件循环)和for(迭代循环)。break用于退出循环,continue用于跳过当前迭代。

rust 复制代码
fn main() {
    // loop循环
    let mut count = 0;
    let result = loop {
        count += 1;
        if count == 5 {
            break count * 2;  // 退出循环并返回值
        }
    };
    println!("loop循环结果: {}", result);  // 输出: 10
    
    // while循环
    let mut number = 10;
    while number > 0 {
        println!("{}", number);
        number -= 1;
    }
    
    // continue示例
    println!("1-10中的奇数:");
    let mut i = 0;
    while i < 10 {
        i += 1;
        if i % 2 == 0 {
            continue;  // 跳过偶数
        }
        println!("{}", i);
    }
}
for 循环与迭代器

Rust 的 for 循环主要用于遍历可迭代对象(如数组、向量、Range 等),语法简洁且安全,避免了 C 风格循环的越界风险。

rust 复制代码
fn main() {
    // 遍历数组
    let numbers = [10, 20, 30, 40, 50];
    for num in numbers {
        println!("数字: {}", num);
    }
    
    // 遍历Range
    println!("1-10的数字:");
    for i in 1..11 {  // 1到10(不包含11)
        println!("{}", i);
    }
    
    // 遍历并获取索引
    let names = ["Alice", "Bob", "Charlie"];
    for (index, name) in names.iter().enumerate() {
        println!("索引 {}: {}", index, name);
    }
}

2. IO 操作与用户输入

std::io 模块基础

Rust 的 IO 操作主要通过std::io模块实现,该模块提供了处理输入输出的各种工具,包括读取用户输入、文件操作等。

rust 复制代码
use std::io;
fn main() {
    println!("请输入你的名字:");
    
    // 创建标准输入的引用
    let stdin = io::stdin();
    
    // 读取一行输入,返回Result类型
    let mut name = String::new();
    stdin.read_line(&mut name)
        .expect("读取输入失败");
    
    // 去除输入中的换行符
    let name = name.trim();
    println!("你好,{}!", name);
}
BufRead trait 与 read_line 方法

BufRead trait 提供了从输入流中读取数据的方法,read_line用于读取一行输入并存储到String中。

rust 复制代码
use std::io::{self, BufRead};
fn main() {
    // 获取标准输入的句柄并转换为BufRead
    let stdin = io::stdin();
    let mut handle = stdin.lock();
    
    let mut line = String::new();
    // 读取一行输入,成功时返回读取的字节数
    match handle.read_line(&mut line) {
        Ok(bytes) if bytes == 0 => println!("输入结束"),
        Ok(bytes) => println!("读取了 {} 个字节: {}", bytes, line),
        Err(e) => println!("读取错误: {}", e),
    }
}
输入数据的解析与错误处理

用户输入的内容通常是字符串,需要解析成其他类型(如数字),解析过程中需要处理可能的错误。

rust 复制代码
use std::io;
fn main() {
    println!("请输入一个数字:");
    let mut input = String::new();
    io::stdin().read_line(&mut input)
        .expect("读取输入失败");
    
    // 去除换行符
    let input = input.trim();
    
    // 解析为i32类型,返回Result<i32, ParseIntError>
    match input.parse::<i32>() {
        Ok(num) => println!("你输入的数字是: {}", num),
        Err(_) => println!("输入不是有效的数字"),
    }
}

3. 集合类型:HashMap 的基本使用

HashMap 的创建与插入

HashMap是 Rust 中基于哈希表实现的键值对集合,需要引入std::collections::HashMap。

rust 复制代码
use std::collections::HashMap;
fn main() {
    // 创建空的HashMap
    let mut scores = HashMap::new();
    
    // 插入键值对
    scores.insert("Alice", 85);
    scores.insert("Bob", 92);
    scores.insert("Charlie", 78);
    
    // 另一种创建方式
    let team_scores = vec![("Alice", 85), ("Bob", 92), ("Charlie", 78)];
    let scores: HashMap<_, _> = team_scores.into_iter().collect();
    
    println!("初始分数: {:?}", scores);
}
HashMap 的查询与更新

可以通过get方法查询值,通过insert方法更新或插入键值对,还可以使用entry API 进行更灵活的更新操作。

rust 复制代码
use std::collections::HashMap;
fn main() {
    let mut scores = HashMap::new();
    scores.insert("Alice", 85);
    scores.insert("Bob", 92);
    
    // 查询值
    if let Some(score) = scores.get("Alice") {
        println!("Alice的分数: {}", score);
    }
    
    // 更新值
    scores.insert("Alice", 90);  // 替换现有值
    println!("更新后Alice的分数: {}", scores.get("Alice").unwrap());
    
    // 仅在键不存在时插入
    scores.entry("Charlie").or_insert(75);
    println!("Charlie的分数: {}", scores.get("Charlie").unwrap());
    
    // 根据现有值更新
    scores.entry("Bob").and_modify(|score| *score += 5);
    println!("Bob的分数: {}", scores.get("Bob").unwrap());
}
HashMap 的遍历

可以通过iter()方法遍历 HashMap 的键值对,也可以单独遍历键或值。

rust 复制代码
use std::collections::HashMap;
fn main() {
    let scores = HashMap::from([
        ("Alice", 85),
        ("Bob", 92),
        ("Charlie", 78),
    ]);
    
    // 遍历所有键值对
    println!("所有分数:");
    for (name, score) in &scores {
        println!("{}: {}", name, score);
    }
    
    // 遍历所有键
    println!("学生名单:");
    for name in scores.keys() {
        println!("{}", name);
    }
    
    // 遍历所有值
    println!("分数列表:");
    for score in scores.values() {
        println!("{}", score);
    }
}

项目实战:实现任务管理交互逻辑

1. 定义 TaskManager 结构体

在src目录下创建cli.rs文件,定义TaskManager结构体用于管理任务集合,使用 HashMap 存储任务。

rust 复制代码
// src/cli.rs
use crate::task::{Task, TaskStatus};
use std::collections::HashMap;
use std::time::SystemTime;
pub struct TaskManager {
    tasks: HashMap<u32, Task>,
    next_id: u32,
}
impl TaskManager {
    // 创建新的TaskManager实例
    pub fn new() -> Self {
        TaskManager {
            tasks: HashMap::new(),
            next_id: 1,
        }
    }
}

2. 实现添加任务功能

在TaskManager中添加add_task方法,用于创建并存储新任务。

rust 复制代码
// src/cli.rs
impl TaskManager {
    // ... 已有代码 ...
    
    // 添加新任务
    pub fn add_task(&mut self, title: String, description: String, due_date: SystemTime) {
        let task = Task::new(
            self.next_id,
            title,
            description,
            due_date,
        );
        self.tasks.insert(self.next_id, task);
        println!("任务已添加,ID: {}", self.next_id);
        self.next_id += 1;
    }
}

3. 实现列出任务功能

添加list_tasks方法,用于遍历并显示所有任务信息。

rust 复制代码
// src/cli.rs
impl TaskManager {
    // ... 已有代码 ...
    
    // 列出所有任务
    pub fn list_tasks(&self) {
        if self.tasks.is_empty() {
            println!("暂无任务");
            return;
        }
        
        println!("ID\t状态\t标题\t\t截止日期");
        println!("----------------------------------------");
        
        for (_, task) in &self.tasks {
            let status = match task.status {
                TaskStatus::Todo => "待办",
                TaskStatus::InProgress => "进行中",
                TaskStatus::Completed => "已完成",
            };
            // 将SystemTime转换为字符串(简化处理,实际项目中应使用更完善的日期格式)
            let due_date = match task.due_date.duration_since(SystemTime::UNIX_EPOCH) {
                Ok(dur) => format!("{}", dur.as_secs()),
                Err(_) => "未知日期".to_string(),
            };
            println!("{}\t{}\t{}\t{}", task.id, status, task.title, due_date);
        }
    }
}

4. 实现简单的命令行菜单

main.rs中编写命令行交互逻辑,实现菜单显示和用户输入处理。

rust 复制代码
// src/main.rs
mod task;
mod cli;
use cli::TaskManager;
use task::TaskStatus;
use std::io::{self, BufRead};
use std::time::SystemTime;
fn main() {
    let mut manager = TaskManager::new();
    let stdin = io::stdin();
    let mut input = String::new();
    
    loop {
        println!("\n===== RustTask 命令行工具 =====");
        println!("1. 添加任务");
        println!("2. 列出所有任务");
        println!("3. 更新任务状态");
        println!("4. 退出");
        println!("请输入命令 (1-4):");
        
        // 清空input并读取用户输入
        input.clear();
        stdin.lock().read_line(&mut input).unwrap();
        let command = input.trim().parse::<u32>().unwrap_or(0);
        
        match command {
            1 => {
                // 添加任务
                println!("请输入任务标题:");
                let mut title = String::new();
                stdin.lock().read_line(&mut title).unwrap();
                title = title.trim().to_string();
                
                println!("请输入任务描述:");
                let mut description = String::new();
                stdin.lock().read_line(&mut description).unwrap();
                description = description.trim().to_string();
                
                // 简化处理截止日期,实际项目中应使用更完善的日期解析
                let due_date = SystemTime::now() + std::time::Duration::from_secs(86400);  // 24小时后
                
                manager.add_task(title, description, due_date);
            },
            2 => {
                // 列出任务
                manager.list_tasks();
            },
            3 => {
                // 更新任务状态(简化处理,后续章节完善)
                println!("请输入任务ID:");
                let mut id_str = String::new();
                stdin.lock().read_line(&mut id_str).unwrap();
                let task_id = id_str.trim().parse::<u32>().unwrap_or(0);
                
                println!("请输入新状态 (1: 待办, 2: 进行中, 3: 已完成):");
                let mut status_str = String::new();
                stdin.lock().read_line(&mut status_str).unwrap();
                let status = status_str.trim().parse::<u32>().unwrap_or(0);
                
                let new_status = match status {
                    1 => TaskStatus::Todo,
                    2 => TaskStatus::InProgress,
                    3 => TaskStatus::Completed,
                    _ => {
                        println!("无效状态");
                        continue;
                    }
                };
                
                // 后续章节实现更新逻辑
                println!("更新任务ID {} 状态为: {:?}", task_id, new_status);
            },
            4 => {
                // 退出程序
                println!("感谢使用,再见!");
                break;
            },
            _ => {
                println!("无效命令,请重试");
            }
        }
    }
}

5. 编译与测试

编译并运行程序,测试添加任务和列出任务功能:

arduino 复制代码
cargo build
cargo run

程序运行示例

rust 复制代码
===== RustTask 命令行工具 =====
1. 添加任务
2. 列出所有任务
3. 更新任务状态
4. 退出
请输入命令 (1-4):
1
请输入任务标题:
学习Rust
请输入任务描述:
完成第三章内容
===== RustTask 命令行工具 =====
1. 添加任务
2. 列出所有任务
3. 更新任务状态
4. 退出
请输入命令 (1-4):
2
ID        状态        标题                截止日期
----------------------------------------
1        待办        学习Rust        1687593600
===== RustTask 命令行工具 =====
1. 添加任务
2. 列出所有任务
3. 更新任务状态
4. 退出
请输入命令 (1-4):
4
感谢使用,再见!

实践作业

完善命令行交互,添加删除任务功能并处理用户确认操作,具体要求:

  1. 在TaskManager中添加remove_task方法,根据 ID 删除任务
  1. 在命令行菜单中添加删除任务选项(命令 5)
  1. 实现删除任务时的用户确认功能,避免误操作
  1. 处理删除不存在任务时的错误情况
  1. main.rs中测试删除任务功能
rust 复制代码
// 在TaskManager中添加remove_task方法
// 在main.rs中添加删除任务的交互逻辑
fn main() {
    // 测试删除任务功能
}

通过完成这个作业,你将进一步巩固流程控制、IO 操作和 HashMap 集合的使用,学习如何实现更完善的命令行交互逻辑。

相关推荐
加班是不可能的,除非双倍日工资1 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi2 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip2 小时前
vite和webpack打包结构控制
前端·javascript
excel2 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国3 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼3 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy3 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
ZXT3 小时前
promise & async await总结
前端
Jerry说前后端3 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化
画个太阳作晴天3 小时前
A12预装app
linux·服务器·前端