Reputation: 3150
I basically have the following code that needs to modify a vector field (by removing some items through the retains
function) and in the predicate of that retains
function, I need to call a mutable function on self :
struct Task {}
struct Processor {
counter: u32,
tasks: Vec<Task>
}
impl Processor {
fn execute(&mut self, task: &Task) -> bool {
self.counter += 1;
// Do some stuff with task and return wether it was sucessfully executed
true
}
fn run(&mut self) {
self.tasks.retain(|task| !self.execute(task))
}
}
The compiler complains with two errors:
error[E0501]: cannot borrow `self.tasks` as mutable because previous closure requires unique access
--> src/main.rs:90:9
|
90 | self.tasks.retain(|task| !self.execute(task))
| ^^^^^^^^^^^------^------^^----^^^^^^^^^^^^^^^
| | | | |
| | | | first borrow occurs due to use of `self` in closure
| | | closure construction occurs here
| | first borrow later used by call
| second borrow occurs here
error[E0500]: closure requires unique access to `self` but it is already borrowed
--> src/main.rs:90:27
|
90 | self.tasks.retain(|task| !self.execute(task))
| ---------- ------ ^^^^^^ ---- second borrow occurs due to use of `self` in closure
| | | |
| | | closure construction occurs here
| | first borrow later used by call
| borrow occurs here
I do understand the issue, but how do I make this work ?
Upvotes: 4
Views: 1841
Reputation: 43753
Option 1: Split your struct into the right parts so they can be borrowed separately. The key part here is that execute
doesn't take &mut Processor
, only &mut Engine
.
struct Task {}
struct Engine {
counter: u32,
}
struct Processor {
engine: Engine,
tasks: Vec<Task>
}
impl Engine {
fn execute(&mut self, task: &Task) -> bool {
self.counter += 1;
true
}
}
impl Processor {
fn run(&mut self) {
let engine = &mut self.engine;
self.tasks.retain(|task| !engine.execute(task))
}
}
Option 2: Move the tasks vector out with std::mem::take, so they're temporarily owned by the function instead of the Processor
, then put them back:
impl Processor {
fn run(&mut self) {
let mut tasks = std::mem::take(&mut self.tasks);
tasks.retain(|task| !self.execute(task));
self.tasks = tasks;
}
}
(This does not copy the memory for all the tasks, because the Vec
's heap allocated storage is undisturbed by moving the Vec
value itself.)
Option 3: If in a more complex situation you can't find a split between parts or a temporary move that works for all your operations, then you could use RefCell
to do run-time instead of compile-time borrow checking — you'd need one cell for tasks
and another cell or cells for the parts execute
needs.
Upvotes: 3
Reputation: 15012
Rust doesn't yet have a way of only borrowing certain members of self
. Because of this, it can't infer that you aren't mutating both self.tasks
and self.counter
at the same time. All it knows is that you're trying to mutate self
twice at the same time.
To work around this, you'll need to move tasks
out of self
, then perform the operation you want, then move it back into self
.
Rust has a handy operation that does exactly this in the standard library. Since Vec
implements Default
(yielding an empty vector, no allocation necessary), we can use std::mem::take
:
fn run(&mut self) {
// swaps `self.tasks` with an empty `Vec`, and yields `tasks` to us
// this won't allocate, because `Vec::new` and therefore `Vec::default` do not
let mut tasks = std::mem::take(&mut self.tasks);
// do what we want with it
tasks.retain(|task| !self.execute(task));
// move it back into `self`
self.tasks = tasks;
}
Upvotes: 6