我在Rust中编写了一些连接到远程服务器的代码,根据该服务器发送的消息,计算一些统计信息或根据这些统计信息执行操作 . 但这对我来说更像是一个学习项目,而且我遇到了一个问题 .
这是我已经减少到最低限度以重现问题的代码:
// 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行循环(我更愿意理解实际解决方案的样子) .
3 回答
当你在借用
self
的一部分时调用&mut self
方法,所以你需要以某种方式重组 .我这样做的方法是将
process_message
所需的状态拆分为一个单独的类型(在您的示例中基本上是HashMap
,但在实际应用程序中它可能包含更多),并将方法移动到该类型 . 这是因为you can separately borrow fields from a struct .另一种选择(在你的真实例子中可能或不可能)是避免在循环中借用,使其更像:
鉴于您不需要完整的
ServerReader
来处理消息,您可以使process_message
成为自由函数并将&mut self.counters
传递给它 . 然后你有server
和counters
的不相交借,这很好 .或者,如果
ServerReader
的非_1170158_的较大部分,请将其提取到自己的结构中,并使process_message
成为该结构的impl方法 .为了允许
Iterator
中的可变性,您应该使用iter_mut()
并处理可变引用(&mut message
) . 然后,为了避免额外借用,您可以在循环体中执行添加: