1098 字
约 3 分钟
1
查找 Todo

查找 Todo

我们将 create 命令功能基本完善了, 它可以使用命令行参数和交互式界面两种方式来创建 Todo 项。

但是我们的 list 命令还相对简陋, 它只能列出所有的 Todo 项, 而无法根据条件筛选 Todo 项。 因此我们需要完善它。

筛选 Todo

我们将创建一个 TodoItemFilter 结构体。用来表示筛选配置。

pub struct TodoItemFilter {
  pub title: Option<String>,
  pub content: Option<String>,
}

TodoItemFilter 将具有两个属性, 分别代表需要筛选的 titlecontent 内容。 因为可能只需要筛选其中一个, 因此将两者均设为 Option<String> 属性表示可选的。

接下来我们将为它实现一些方法。

首先是实例化方法。

impl TodoItemFilter {
  pub fn new() -> Self {
    Self {
      title: Option::None,
      content: Option::None,
    }
  }
}

在一开始, 我们并没有办法知道需要筛选什么内容, 因此将 titlecontent 均设为 None

泛型约束

在之前代码中, 我们了解到了用来占位表示任何类型的泛型。 但仅仅只是一个 T 的范围就太大了。例如我们可能需要的参数是一个字符串, 但却可以将数字, 布尔传递进来。

为解决这个问题, Rust 支持对泛型进行类型约束, 使得泛型参数必须满足特定的条件。 因此我们需要对泛型进行进一步的约束。

例如, 我们希望参数可以转换为字符串类型, 那么可以使用 Into<String> 作为约束条件。 Into<String> 表示“任何可以转换成 String 的类型”。

impl TodoItemFilter {
  // ...
  pub fn set_title<T: Into<String>>(&mut self, title: T) {
    self.title = Some(title.into());
  }

  pub fn set_content<T: Into<String>>(&mut self, content: T) {
    self.content = Some(content.into());
  }
}

上方代码分别为 TodoItemFilter 实现 set_titleset_content 方法。 我们不关心参数 T 具体是什么类型, 只要它满足约束条件 Into<String> 就行。 无论 TString 类型, 还是 &str 类型, 抑或是 Vec<u8> 类型, 只要它可以被转换为 String 类型, 都可以作为参数传入。

类型约束有两种写法, 上面的是第一种, 直接将约束条件写在泛型参数位置, 更加常用。 第二种则是使用 where 子句单独列出, 适用于复杂约束情况。

impl TodoItemFilter {
  // ...
  pub fn set_title<T>(&mut self, title: T) where T: Into<String> {
    self.title = Some(title.into());
  }

  pub fn set_content<T>(&mut self, content: T) where T: Into<String> {
    self.content = Some(content.into());
  }
}

筛选参数

我们目前的 Todo 项具有两项属性, 即 titlecontent。 我们可以根据这两项属性中的任意一项或两项来筛选 Todo 项。

我们为 TodoItemFilter 实现一个 filter 方法用于过滤 Todo 项。

pub fn filter(&self, list: &Vec<TodoItem>) {
  let mut filtered_list = Vec::<&TodoItem>::new();

  if self.title.is_none() && self.content.is_none() {
    for item in list {
      filtered_list.push(item);
    }
  } else {
    for item in list {
      let mut flag: (bool, bool) = (false, false);

      flag.0 = match &self.title {
        Some(title) => item.title.contains(title),
        _ => true,
      };

      flag.1 = match &self.content {
        Some(content) => item.content.contains(content),
        _ => true,
      };

      if flag.0 && flag.1 {
        filtered_list.push(item);
      }
    }
  }

  for item in filtered_list {
    println!("todo title: {}, content: {}", item.title, item.content);
  }
}

以上代码为 TodoItemFilter 实现的 filter 方法。 它首先创建一个空的 filtered_list 用于存储筛选后的 Todo 项。 如果 titlecontent 均为空, 则直接将所有 Todo 项放入 filtered_list。 否则, 遍历所有 Todo 项, 根据 titlecontent 进行筛选。 如果 Todo 项的 titlecontent 均包含筛选条件, 则将其放入 filtered_list

最后, 遍历 filtered_list, 打印出筛选后的 Todo 项。

然后我们改造 TodoCommand 枚举, 为 list 命令增加两个参数。

// ...
/// List all todo items
List {
  #[arg(short, long)]
  title: Option<String>,
  #[arg(short, long)]
  content: Option<String>,
}
// ...

改造 main 函数, 在 list 命令中添加筛选参数。

// ...
  match args.command {
    TodoCommand::Create { title, content } => {
      todo::create::create_todo(&mut todos, title, content)
    }
    TodoCommand::List { title, content } => todo::list::list_todo(&todos, title, content),
  }
// ...

接着改造 list_todo 函数。

pub fn list_todo(todos: &Vec<TodoItem>, title: Option<String>, content: Option<String>) {
  let mut filter = TodoItemFilter::new();

  if let Some(title) = title {
    filter.set_title(title);
  }

  if let Some(content) = content {
    filter.set_content(content);
  }

  filter.filter(todos);
}

现在, 当我们执行 cargo run -- list 命令时, 可以携带 --title--content 参数进行筛选了。

查找 Todo
http://www.clxhxhhr.top/posts/451/
作者
clxstart
发布于
2026-09-04
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。
文章目录