如何在 Rust 中迭代 vec 并进行更改?

问题描述 投票:0回答:1

我正在尝试使用 Rust 构建一个任务管理器来进行练习。

我想要一个存档的任务部分,所以这是我的任务管理器结构:

pub struct TaskManager {
    tasks: Vec<Task>,
    archived_tasks: Vec<Task>,
}

我有公开任务和存档任务的功能:

impl TaskManager {
    pub fn tasks(&self) -> &Vec<Task> {
        &self.tasks
    }

    pub fn tasks_mut(&mut self) -> &mut Vec<Task> {
        &mut self.tasks
    }

    pub fn archived_tasks(&self) -> &Vec<Task> {
        &self.archived_tasks
    }

    pub fn archived_tasks_mut(&mut self) -> &mut Vec<Task> {
        &mut self.archived_tasks
    }
}

然后,我有一个可以归档有效任务的功能:

impl TaskManager {
    pub fn archive_task(&mut self, idx: usize) -> Result<(), String> {
        match self.tasks.get(idx) {
            Some(_) => {
                self.archived_tasks.push(self.tasks.remove(idx));
                Ok(())
            },
            None => Err("No task with that index".to_string())
        }
    }
}

然后...借用检查器。我无法执行此操作或类似操作,因此我无法迭代我的任务并调用

archive_task
:

impl App {
    fn archive_done_tasks(&mut self) {
        for (idx, task) in self.task_manager.tasks().iter().enumerate() {
            if task.done() {
                self.task_manager.archive_task(idx);
            }
        }
    }
}

编译器当然会抱怨:

error[E0502]: cannot borrow `self.task_manager` as mutable because it is also borrowed as immutable
   --> src/app.rs:194:17
    |
192 |         for (idx, task) in self.task_manager.tasks().iter().enumerate() {
    |                            --------------------------------------------
    |                            |
    |                            immutable borrow occurs here
    |                            immutable borrow later used here
193 |             if task.done() {
194 |                 self.task_manager.archive_task(idx);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

有什么我遗漏的或者我想做的事情是不可能的吗?

rust borrow-checker
1个回答
0
投票

正如错误消息所指出的,如果 task_manager 已被借用,则无法获得对它的可变引用。

事实上,这个编译器错误阻止了代码中的错误。

如果允许的话,

archive_task
将从
tasks
向量中删除项目,而
archive_done_tasks
则对其进行迭代。这会导致一些严重的错误,但幸运的是 rust 可以防止此类问题。

避免添加或删除项目的常见模式是将逻辑分为两遍

  1. 识别要删除的索引
  2. 删除索引

请记住,当删除元素时,它会将每个元素的索引更改为右侧。

let mut indices: Vec<usize> = tm.tasks().iter().enumerate()
    .filter(|(_, task)| task.done())
    .map(|(idx, _)| idx)
    .collect();
indices.sort();
for (offset, idx) in indices.into_iter().enumerate() {
    tm.archive_task(idx-offset);
}
© www.soinside.com 2019 - 2024. All rights reserved.