I have a struct
that holds mutable references to trait objects:
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
:
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. I also need tasks to be mutable. How do I do it in Rust?
You are calling
tasks.iter()
which produces immutable references to the elements ofVec
. You actually get back&&mut Task
, an immutable reference to a mutable one (that is why the Rust compiler is complaining).To solve this, call
tasks.iter_mut()
to get an iterator of mutable references.The second problem is calling defining
work_one
as a method. You already borrow a mutable reference fromself
when iterating, so you cannot get another borrow.Working example (playground):
To still have access to
self
inwork_one
this workaround can be used. This basically just swaps the two vectors so you do not actually borrowself
when iterating and then swapping it back. This is ugly, there may be a better pattern here, maybe someone else will suggest something better.A nicer alternative suggested by @Veedrac:
You need to have a mutable reference to each item.
iter
returns immutable references. And a immutable reference to a mutable variable is not itself mutable. Useiter_mut
orfor task in &mut self.tasks
instead.Then, the easiest thing to do is to inline
work_one
intowork
:Unfortunately, splitting this into two functions is quite painful. You have to guarantee that calling
self.work_one
will not modifyself.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.See also: