简体   繁体   English

遍历对特征对象的可变引用的向量

[英]Iterating over a vector of mutable references to trait objects

I have a struct that holds mutable references to trait objects: 我有一个struct ,其中包含对特征对象的可变引用:

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

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

In a method of Worker , I want to iterate over the tasks and call their do_it : 在一种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();
    }
}

Sadly, the borrow checker does not let me do it: 可悲的是,借阅检查器不允许我这样做:

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

I cannot make Worker generic because I want it to hold tasks of many types. 我无法使Worker通用性,因为我希望它可以容纳许多类型的任务。 I also need tasks to be mutable. 我还需要可变的任务。 How do I do it in Rust? 如何在Rust中完成?

You are calling tasks.iter() which produces immutable references to the elements of Vec . 您正在调用tasks.iter() ,它生成对Vec元素的不可变引用。 You actually get back &&mut Task , an immutable reference to a mutable one (that is why the Rust compiler is complaining). 您实际上返回了&&mut Task ,它是对可变&&mut Task的不变引用(这就是Rust编译器抱怨的原因)。

To solve this, call tasks.iter_mut() to get an iterator of mutable references. 要解决此问题,请调用tasks.iter_mut()以获得可变引用的迭代器。

The second problem is calling defining work_one as a method. 第二个问题是调用将work_one定义为方法。 You already borrow a mutable reference from self when iterating, so you cannot get another borrow. 迭代时,您已经从self借来了可变的引用,因此您无法再借用。

Working example ( playground ): 工作示例( 操场 ):

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();
    }
}

To still have access to self in work_one this workaround can be used. 要仍然在work_one访问self ,可以使用此替代方法。 This basically just swaps the two vectors so you do not actually borrow self when iterating and then swapping it back. 基本上,这只是交换两个向量,因此您在迭代然后将其交换回时实际上并不借用self This is ugly, there may be a better pattern here, maybe someone else will suggest something better. 这很丑陋,这里可能会有更好的模式,也许其他人会提出更好的建议。

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);
}

A nicer alternative suggested by @Veedrac: @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;
}

You need to have a mutable reference to each item. 您需要对每个项目都有可变的引用。 iter returns immutable references. iter返回不可变的引用。 And a immutable reference to a mutable variable is not itself mutable. 对可变变量的不可变引用本身并不是可变的。 Use iter_mut or for task in &mut self.tasks instead. 请使用iter_mut或将其for task in &mut self.tasks

Then, the easiest thing to do is to inline work_one into work : 然后, 最简单的方法是将work_one内联到work

pub fn work(&mut self) {
    for task in self.tasks.iter_mut() {
        task.do_it()
    }
}

Unfortunately, splitting this into two functions is quite painful. 不幸的是,将其分为两个功能非常痛苦。 You have to guarantee that calling self.work_one will not modify self.tasks . 您必须保证调用self.work_one不会修改self.tasks Rust doesn't track these things across function boundaries, so you need to split out all the other member variables and pass them separately to a function. Rust不会跨函数边界跟踪这些事情,因此您需要拆分所有其他成员变量并将其分别传递给函数。

See also: 也可以看看:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM