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 ?
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;
}
Playground
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.
Related
As you can see in the following code, I have two traits, one is called Hittable, and the other is called Material (I have been studying the book "ray-tracing-in-one-weekend", but use Rust).
The Hittable trait implements hit function for some objects (just like Sphere in this code), and every kind of objects includes its material (just like Glass, Wood...).
In my real project, the Sphere struct and another struct (called HitRecord in this book, used as mut reference to pass result in hit function), they both include &dyn Material, so that I need add lifetime parameter for both of them. However, to accomplish that, I should add lifetime parameter in the trait declaration, so I can assign the same lifetime parameter for Sphere and hit.
But the compiler indicates that the reference still under borrowed when the main function ends, I have no idea for that...
trait Hittable<'a> {
fn hit(&self);
}
trait Material {
fn result(&self);
}
struct Glass;
impl Material for Glass {
fn result(&self) {
println!("Glass is broken!");
}
}
struct Sphere<'a> {
name: String,
mat_ptr: &'a dyn Material,
}
impl<'a> Hittable<'a> for Sphere<'a> {
fn hit(&self) {
println!("Name is {}", self.name);
self.mat_ptr.result();
}
}
struct HT<'a> {
pub objects: Vec<Box<dyn Hittable<'a>>>,
}
fn main() {
let mut list = HT { objects: vec![] };
let surface_material = Glass;
let s = Sphere {
name: String::from("球"),
mat_ptr: &surface_material,
};
list.objects.push(Box::new(s));
}
the message shows
Compiling rust_test v0.1.0 (/home/hnyls2002/rust_test)
error[E0597]: `surface_material` does not live long enough
--> src/main.rs:38:18
|
38 | mat_ptr: &surface_material,
| ^^^^^^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| cast requires that `surface_material` is borrowed for `'static`
...
41 | }
| - `surface_material` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
This is because dyn Hittable<'a> is actually dyn Hittable<'a> + 'static, and thus s is required to live for 'static. The fix is to change HT to:
struct HT<'a> {
pub objects: Vec<Box<dyn Hittable<'a> + 'a>>,
}
Then you'll get a long but pretty self-explanatory error:
error[E0597]: `surface_material` does not live long enough
--> src/main.rs:38:18
|
38 | mat_ptr: &surface_material,
| ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
41 | }
| -
| |
| `surface_material` dropped here while still borrowed
| borrow might be used here, when `list` is dropped and runs the destructor for type `HT<'_>`
|
= note: values in a scope are dropped in the opposite order they are defined
Because surface_material is defined after list, it'll be dropped before and when the potential destructor for dyn Hittable will run it may access the freed surface_material. The fix is just to swap the declaration order of list and surface_material:
fn main() {
// always define before the container you are pushing into
let surface_material = Glass;
let mut list = HT { objects: vec![] };
// ...
}
This question already has answers here:
Double mutable borrow error in a loop happens even with NLL on
(2 answers)
Closed 11 months ago.
I'm working with a library that offers a RAII transaction API shaped like this:
struct Dataset {}
struct Transaction<'a> {
dataset: &'a mut Dataset,
}
struct Error;
impl Dataset {
fn start_transaction(&mut self) -> Result<Transaction<'_>, Error> {
Ok(Transaction { dataset: self }) // In real life, may also return Err.
}
}
Now I want to write some wrapper code that tries to start a Transaction on a dataset, but if that fails because the underlying data source does not support transactions, it returns the Dataset as-is. (Then everything is eventually resolved to a Dataset through DerefMut implementations, but that's beside the point.)
Here's my attempt:
enum TransactionIfSupported<'a> {
Transaction(Transaction<'a>),
Dataset(&'a mut Dataset),
}
fn start_transaction_if_supported<'a>(dataset: &'a mut Dataset) -> TransactionIfSupported<'a> {
if let Ok(txn) = dataset.start_transaction() {
return TransactionIfSupported::Transaction(txn);
}
TransactionIfSupported::Dataset(dataset)
}
Sadly the borrow checker frowns upon it:
error[E0499]: cannot borrow `*dataset` as mutable more than once at a time
--> src/lib.rs:28:37
|
24 | fn start_transaction_if_supported<'a>(dataset: &'a mut Dataset) -> TransactionIfSupported<'a> {
| -- lifetime `'a` defined here
25 | if let Ok(txn) = dataset.start_transaction() {
| --------------------------- first mutable borrow occurs here
26 | return TransactionIfSupported::Transaction(txn);
| ---------------------------------------- returning this value requires that `*dataset` is borrowed for `'a`
27 | }
28 | TransactionIfSupported::Dataset(dataset)
| ^^^^^^^ second mutable borrow occurs here
I don't quite understand why this happens. If start_transaction returns the Err variant, I'd expect any borrows from the start_transaction call to no longer be in scope after the if block. I'd expect this to hold even if there was no unconditional return statement inside the if.
What's going on and how do I resolve it?
Playground link
It looks like the borrow checker is not smart enough to handle this case yet. The linked question contains a solution for the HashMap case specifically, which will not work here because there is no contains_key equivalent.
Instead, we need some unsafe code to express it:
fn start_transaction_if_supported<'a, 'b>(dataset: &'b mut Dataset) -> TransactionIfSupported<'a, 'b> {
unsafe {
let dataset_ptr: *mut Dataset = dataset;
if let Ok(txn) = (*dataset_ptr).start_transaction() {
return TransactionIfSupported::Transaction(txn);
}
TransactionIfSupported::Dataset(&mut *dataset)
}
}
I'm holding a reference to a Struct inside another Struct, both of which are declared in the same block.
I later want to use the outer Struct in a closure which is run repeatedly and never returns.
The reference inside the Struct apparently does not live long enough, but in my understanding, it should never go out of scope, or at least should live at least as long as the Struct it is referring to:
struct MyStruct;
struct ReferenceStruct<'a> {
reference: &'a MyStruct
}
impl<'a> ReferenceStruct<'a> {
fn do_something(&self) -> () {}
}
fn run<F>(mut function: F) -> !
where
F: FnMut() -> () + 'static
{
loop {
function();
}
}
fn main() {
let my_struct = MyStruct;
let reference = ReferenceStruct { reference: &my_struct };
run(move || {
reference.do_something();
});
}
(link to playground)
The run function (for context) mirrors an event loop, similar to that of Winit, and in reality, I have another Struct which owns the value being referenced, but this example reproduces it in fewer lines.
The error:
error[E0597]: `my_struct` does not live long enough
--> src\main.rs:26:50
|
26 | let reference = ReferenceStruct { reference: &my_struct };
| ^^^^^^^^^^ borrowed value does not live long enough
27 |
28 | / run(move ||
29 | | {
30 | | reference.do_something();
31 | | });
| |______- argument requires that `my_struct` is borrowed for `'static`
32 | }
| - `my_struct` dropped here while still borrowed
It appears that my_struct is dropped at the end of main, but even if the program flow somehow escapes the loop, it surely lasts as long as the reference struct, which is as long as it needs to. I don't understand where or how this error could come about, or what to do about it.
Your problem is the lifetime bound 'static for the closure passed into run(). This means that the lifetime of reference is also 'static because it is moved into the closure, which in turn means that my_struct must also have static lifetime – and that is not the case.
Luckily, you do not need the 'static bound here. If you remove it, everything works:
...
fn run<F>(mut function: F) -> !
where
F: FnMut() -> ()
...
However, this might not be a solution in your use case if the event loop needs the closure to be 'static.
I'm trying to call a mutable method inside a immutable scope with gives me: error[E0502].
I'm already understand why this error occurs but I'm struggle on how to fix it or how to do a different approach that works.
Here a MCVE
struct A {
list: Vec<i8>,
has_three: bool,
}
impl A {
pub fn new() -> Self {
Self {
list: vec![1,2,3],
has_three: false,
}
}
pub fn mutable_method(&mut self) {
for item in self.list.iter() {
self.mutable_method2(item);
}
}
fn mutable_method2(&mut self, item: &i8) {
let b: i8 = 3;
if item == &b {
self.has_three = true;
}
}
}
fn main() {
let mut a = A::new();
a.mutable_method();
}
And the Error received:
Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:15:13
|
14 | for item in self.list.iter() {
| ----------------
| |
| immutable borrow occurs here
| immutable borrow later used here
15 | self.mutable_method2(item);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
You asked for another approach. If I correctly assume that your goal is to have has_three be true if any entry is 3, that's much more easily done this way:
pub fn mutable_method(&mut self) {
self.has_three = self.list.iter().any(|&x| x == 3);
}
By the way, you need to make sure mutable_method is actually called appropriately, otherwise you will end up in logically invalid states. That's not good practice. Consider extracting this to the constructing function already.
Some background
The underlying problem is that your initial approach wants to borrow self mutably, while already borrowing it immutably. However, logically, your code is not obviously invalid, as you only borrow a part of the struct mutably that you do not also borrow immutably. This information is lost though.
We make the safety explicit by factoring into two implicit operations,
let tmp = self.list.iter().any(|&x| x == 3);
self.has_three = tmp;
which both operate on the struct in a 'clear fashion', either mutably or immutably. That is how you can approach such problems.
I'm learning Rust and I'm trying to cargo-cult this code into compiling:
use std::vec::Vec;
use std::collections::BTreeMap;
struct Occ {
docnum: u64,
weight: f32,
}
struct PostWriter<'a> {
bytes: Vec<u8>,
occurrences: BTreeMap<&'a [u8], Vec<Occ>>,
}
impl<'a> PostWriter<'a> {
fn new() -> PostWriter<'a> {
PostWriter {
bytes: Vec::new(),
occurrences: BTreeMap::new(),
}
}
fn add_occurrence(&'a mut self, term: &[u8], occ: Occ) {
let occurrences = &mut self.occurrences;
match occurrences.get_mut(term) {
Some(x) => x.push(occ),
None => {
// Add the term bytes to the big vector of all terms
let termstart = self.bytes.len();
self.bytes.extend(term);
// Create a new occurrences vector
let occs = vec![occ];
// Take the appended term as a slice to use as a key
// ERROR: cannot borrow `*occurrences` as mutable more than once at a time
occurrences.insert(&self.bytes[termstart..], occs);
}
}
}
}
fn main() {}
I get an error:
error[E0499]: cannot borrow `*occurrences` as mutable more than once at a time
--> src/main.rs:34:17
|
24 | match occurrences.get_mut(term) {
| ----------- first mutable borrow occurs here
...
34 | occurrences.insert(&self.bytes[termstart..], occs);
| ^^^^^^^^^^^ second mutable borrow occurs here
35 | }
36 | }
| - first borrow ends here
I don't understand... I'm just calling a method on a mutable reference, why would that line involve borrowing?
I'm just calling a method on a mutable reference, why would that line involve borrowing?
When you call a method on an object that's going to mutate the object, you can't have any other references to that object outstanding. If you did, your mutation could invalidate those references and leave your program in an inconsistent state. For example, say that you had gotten a value out of your hashmap and then added a new value. Adding the new value hits a magic limit and forces memory to be reallocated, your value now points off to nowhere! When you use that value... bang goes the program!
In this case, it looks like you want to do the relatively common "append or insert if missing" operation. You will want to use entry for that:
use std::collections::BTreeMap;
fn main() {
let mut map = BTreeMap::new();
{
let nicknames = map.entry("joe").or_insert(Vec::new());
nicknames.push("shmoe");
// Using scoping to indicate that we are done with borrowing `nicknames`
// If we didn't, then we couldn't borrow map as
// immutable because we could still change it via `nicknames`
}
println!("{:?}", map)
}
Because you're calling a method that borrows as mutable
I had a similar question yesterday about Hash, until I noticed something in the docs. The docs for BTreeMap show a method signature for insert starting with fn insert(&mut self..
So when you call .insert, you're implicitly asking that function to borrow the BTreeMap as mutable.