rust - 从借用它的循环内部操作对象

标签 rust borrow-checker

我正在用 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 行循环结束(而且我更愿意了解实际解决方案的样子)。

最佳答案

正如您所了解的,当您借用 self 的一部分时,您不能调用 &mut 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);
}

关于rust - 从借用它的循环内部操作对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40215216/

相关文章:

rust - 有没有办法为多个特征创建类型别名?

rust - 在 rust 中使用 slice::from_raw_parts_mut 和 ptr::drop_in_place 进行内存释放

rust - 返回一个闭包,返回Rust中的函数参数

string - 查找连续相等字符的最长子串时出现 "borrowed value does not live long enough"错误如何处理?

rust - 在 Rust 中使用引用和使用拥有的值有区别吗?

rust - 你能控制借用结构还是借用字段?

rust - 如何从 Rust 中的不同线程写入文件?

methods - 是否可以编写将扩展为函数/方法签名的 Rust 宏?

rust - 与可变借用相关的不可变借用导致 "cannot borrow ` *self` 一次多次可变”

rust - 是否有必要强制转换为 float 以访问 Rust 中的基本数学函数?