Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Multiple borrows of self with while loops and tokio channels

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

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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() returns Ok and bind it
  • if it is Ok run self.process_response(...)
  • we are at the end of the loop so start again
  • check if rx.try_recv() returns Ok and bind it
  • if it is Ok run self.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() returns Ok and bind it
  • if it is Ok run self.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);
            }
        }
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading