Manipulating an object from inside a loop that borrows it

后端 未结 3 1422
再見小時候
再見小時候 2020-12-20 11:13

I\'m writing some code in Rust that connects to a remote server, and depending on the messages sent by that server, computes some statistics or executes actions based on the

相关标签:
3条回答
  • 2020-12-20 11:30

    Given that you don't need the full ServerReader for processing a message, you could make process_message a free function and just pass &mut self.counters to it. Then you have disjoint borrows of server and counters, which is fine.

    Or if your non-server part of ServerReader is larger, extract that into its own struct, and make process_message an impl method of that struct.

    0 讨论(0)
  • 2020-12-20 11:31

    As you've worked out, you can't call a &mut self method while you're borrowing part of self, so you need to restructure somehow.

    The way I would do it is to split the state needed by process_message into a separate type (in your example that's basically the HashMap, but in the real application it's likely to contain more), and move the method to that type. This works because you can separately borrow fields from a struct.

    struct SomeState {
        counters: HashMap<u32, usize>,
    }
    
    impl SomeState {
        pub fn new() -> SomeState {
            SomeState {
                counters: HashMap::new(),
            }
        }
        fn process_message(&mut self, message: u32) {
            let counter = self.counters.entry(message).or_insert(0);
            *counter += 1;
        }
    }
    
    struct ServerReader {
        server: Vec<u32>,
        state: SomeState,
    }
    
    impl ServerReader {
        fn new() -> ServerReader {
            ServerReader {
                server: vec!(1, 2, 5, 2, 7, 9, 1, 1, 5, 6),
                state: SomeState::new(),
            }
        }
    
        fn run(&mut self) {
            println!("Connecting...");
    
            for message in self.server.iter() {
                println!("Received {}", message);
                self.state.process_message(*message);
            }
            println!("Disconnected");
        }
    
    }
    

    An alternative (which may or may not be possible in your real example) would be to avoid borrowing in the loop, making it more like:

    loop {
        // if next_message() returns an owned message, ie not still borrowing
        // self
        let message = self.next_message();
        // now no borrow left
        self.process_message(message);
    }
    
    0 讨论(0)
  • 2020-12-20 11:41

    In order to allow mutability in an Iterator, you should use iter_mut() and work on mutable references (&mut message). Then, to avoid the additional borrow, you could just perform the addition in the body of the loop:

    for &mut message in self.server.iter_mut() {
        println!("Received {}", message);
        *self.counters.entry(message).or_insert(0) += 1;
    }
    
    0 讨论(0)
提交回复
热议问题