迭代对特征对象的可变引用的向量

Tom*_*omo 8 rust borrow-checker

我有一个struct包含trait对象的可变引用:

trait Task {
    fn do_it(&mut self);
}

struct Worker<'a> {
    tasks: Vec<&'a mut Task>,
}
Run Code Online (Sandbox Code Playgroud)

在一个方法中Worker,我想迭代任务并调用它们do_it:

impl<'a> Worker<'a> {
    pub fn work(&mut self) {
        for task in self.tasks.iter() {
            self.work_one(*task);
        }
    }

    fn work_one(&self, task: &mut Task) {
        task.do_it();
    }
}
Run Code Online (Sandbox Code Playgroud)

可悲的是,借阅检查员不允许我这样做:

error[E0389]: cannot borrow data mutably in a `&` reference
  --> src/main.rs:12:27
   |
12 |             self.work_one(*task);
   |                           ^^^^^ assignment into an immutable reference
Run Code Online (Sandbox Code Playgroud)

我不能制作Worker泛型,因为我希望它能够保存多种类型的任务.我还需要任务变得可变.我怎么在Rust中做到这一点?

Arj*_*jan 15

您正在调用tasks.iter()哪个生成对元素的不可变引用Vec.你真的回来了&&mut Task,一个可变的引用的不可变引用(这就是Rust编译器抱怨的原因).

要解决这个问题,请调用tasks.iter_mut()获取可变引用的迭代器.

第二个问题是将定义work_one称为方法.你已经self在迭代时借用了一个可变引用,所以你不能再获得另一个借用.

工作示例(游乐场):

trait Task {
    fn do_it(&mut self);
}

struct Worker<'a> {
    tasks: Vec<&'a mut Task>,
}

impl<'a> Worker<'a> {
    pub fn work(&mut self) {
        for task in self.tasks.iter_mut() {
            Worker::work_one(*task);
        }
    }

    fn work_one(task: &mut Task) {
        task.do_it();
    }
}
Run Code Online (Sandbox Code Playgroud)

还是要获得selfwork_one此解决方法可以使用.这基本上只是交换两个向量,因此您self在迭代时不会实际借用,然后将其交换回来.这很难看,这里可能有更好的模式,也许其他人会建议更好的东西.

pub fn work(&mut self) {
    let mut tasks = vec![];
    mem::swap(&mut tasks, &mut self.tasks);
    for task in tasks.iter_mut() {
        self.work_one(*task);
    }
    mem::swap(&mut tasks, &mut self.tasks);
}
Run Code Online (Sandbox Code Playgroud)

@Veedrac建议的更好的选择:

fn work(&mut self) {
    let mut tasks = mem::replace(&mut self.tasks, Vec::new());
    for task in &mut tasks {
        self.work_one(*task);
    }
    self.tasks = tasks;
}
Run Code Online (Sandbox Code Playgroud)


She*_*ter 6

您需要对每个项目都有一个可变引用。iter返回不可变的引用。并且对可变变量的不可变引用本身并不可变。使用iter_mutfor task in &mut self.tasks代替。

然后,最简单的事情就是内联work_onework

pub fn work(&mut self) {
    for task in self.tasks.iter_mut() {
        task.do_it()
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,将其分成两个函数是相当痛苦的。你必须保证调用self.work_one不会修改self.tasks。Rust 不会跨函数边界跟踪这些内容,因此您需要拆分所有其他成员变量并将它们单独传递给函数。

也可以看看: