从一个借用它的循环内部操作一个对象

时间:2016-10-24 09:41:50

标签: rust borrow-checker

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

以下是我将代码重复到最低限度以重现问题的代码:

1/3

虽然我认为我理解为什么编译器不满意,但我很难想出一个解决方案。我不能在循环之外操纵我的结构,因为我必须在连接和监听服务器时工作。我也可以将所有内容直接放在循环中而不是调用任何方法,但我不想最终得到1000行循环(我更愿意理解实际解决方案的样子)。

3 个答案:

答案 0 :(得分:16)

正如您已经解决的那样,当您借用&mut self的一部分时,无法调用self方法,因此您需要以某种方式进行重组。

我这样做的方法是将process_message所需的状态拆分为一个单独的类型(在您的示例中基本上是HashMap,但在实际应用程序中它可能包含更多),并将方法移动到该类型。这有效,因为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");
    }

}

另一种选择(在你的实例中可能会或可能不会)可以避免在循环中借用,使其更像:

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

答案 1 :(得分:5)

鉴于您不需要完整的ServerReader来处理邮件,您可以将process_message设为免费功能,然后将&mut self.counters传递给它。然后你有servercounters的不相交借用,这很好。

或者,如果server的非ServerReader部分较大,请将其提取到自己的结构中,并使process_message成为该结构的impl方法。

答案 2 :(得分:3)

为了允许Iterator中的可变性,您应该使用iter_mut()并处理可变引用(&mut message)。然后,为了避免额外的借用,你可以在循环体中执行添加:

for &mut message in self.server.iter_mut() {
    println!("Received {}", message);
    *self.counters.entry(message).or_insert(0) += 1;
}