从借用对象的循环内部操作对象 [英] Manipulating an object from inside a loop that borrows it

查看:27
本文介绍了从借用对象的循环内部操作对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 Rust 编写一些连接到远程服务器的代码,并根据该服务器发送的消息,计算一些统计信息或基于这些统计信息执行操作.但这对我来说更像是一个学习项目,我遇到了一个问题.

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 these statistics. But this is more of a learning project for me and I've run into an issue.

这是我为了重现问题而减少到最低限度的代码:

Here is the code that I have reduced to a bare minimum to reproduce the problem :

// Repro code for error[E0502]: cannot borrow `*self` as mutable because `self.server` is also borrowed as immutable

use std::collections::HashMap;

struct ServerReader {
    server: Vec<u32>, // A vec for demo purposes, but please imagine this is a server object
    counters: HashMap<u32, usize>,
}

impl ServerReader {
    fn new() -> ServerReader {
        ServerReader {
            server: vec!(1, 2, 5, 2, 7, 9, 1, 1, 5, 6), // Filling my "server" with some messages
            counters: HashMap::new(),
        }
    }

    fn run(&mut self) {
        println!("Connecting..."); // ... here there should be some code to connect to the server ...

        for message in self.server.iter() { // We wait for the network messages sent by the server, and process them as they come
//                     ----------- immutable borrow occurs here
            println!("Received {}", message);
            self.process_message(*message); // HOW
//          ^^^^ mutable borrow occurs here
        }
//      - immutable borrow ends here
        println!("Disconnected");
    }

    fn process_message(&mut self, message: u32) {
        // Please imagine that this function contains complex stuff
        let counter = self.counters.entry(message).or_insert(0);
        *counter += 1;
    }
}

fn main() {
    let mut reader = ServerReader::new();

    reader.run();

    println!("Done");
}

虽然我想我明白编译器不满意的原因,但我正在努力想出一个解决方案.我无法在循环之外操纵我的结构,因为我必须在连接和监听服务器的同时工作.我也可以将所有内容直接放入循环中而不调用任何方法,但我不想以 1000 行循环结束(我更愿意了解实际解决方案的样子).

While I think I understand why the compiler is unhappy, I'm struggling to come up with a solution. I cannot manipulate my structure outside of the loop, since I have to work while connected and listening to the server. I also could put everything directly in the loop and not call any method, but I don't want to end up with a 1000 line loop (and I'd prefer to understand what an actual solution would look like).

推荐答案

正如你所发现的,当你借用 &mut self 方法时,你不能调用 &mut self 方法code>self,所以你需要以某种方式重组.

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.

我的做法是将 process_message 所需的状态拆分为一个单独的类型(在您的示例中,基本上是 HashMap,但在实际应用程序中,它是可能包含更多),并将方法移动到该类型.这是有效的,因为您可以单独从结构中借用字段.

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);
}

这篇关于从借用对象的循环内部操作对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆