I’m pretty new to Rust and have code similar to the following.
struct MyStruct {
api_receiver: Option<Receiver<String>>
}
impl MyStruct {
fn process_response(&mut self, payload: String) {
println!("*process_response");
}
pub fn event_pump(&mut self) {
if let Some(rx) = &mut self.api_receiver {
while let Ok(message) = rx.try_recv() {
self.process_response("message".to_string());
}
}
}
}
I have simplified it down for a simple explanation. I’m using Tokio and channels although I don’t think that is relevant here.
The code below gives me the following compiler error
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src\adapter\adapter.rs:95:17
|
93 | if let Some(rx) = &mut self.api_receiver {
| ---------------------- first mutable borrow occurs here
94 | while let Ok(message) = rx.try_recv() {
| ------------- first borrow later used here
95 | self.process_response("message".to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
I’m obviously missing something here. The interesting part is if I change the line while let Ok(message) = rx.try_recv() to if let Ok(message) = rx.try_recv() then everything compiles and works as expected.
What am I missing with regards to loops and borrows?
>Solution :
It’s really quite simple when there is a loop:
while let Ok(message) = rx.try_recv() {
self.process_response("message".to_string());
}
what happens is:
- check if
rx.try_recv()returnsOkand bind it - if it is
Okrunself.process_response(...) - we are at the end of the loop so start again
- check if
rx.try_recv()returnsOkand bind it - if it is
Okrunself.process_response(...) - …
This clearly mixes the calls to rx.try_recv() and self.process_response(...) which both borrow from self mutably but that’s not allowed.
In the if case what happens is this:
- check if
rx.try_recv()returnsOkand bind it - if it is
Okrunself.process_response(...)
No mixing of rx.try_recv() calls and self.process_response(...) so the borrow of self can be released after checking the condition, so there is no overlap of the borrows.
To suggest a solution we would need to know further specifics, but one workaround could be to remove the Receiver from self temporarily for example with Option::take:
pub fn event_pump(&mut self) {
if let Some(mut rx) = self.api_receiver.take() {
while let Ok(message) = rx.try_recv() {
self.process_response("message".to_string());
}
self.api_receiver = Some(rx);
}
}