从借用它的循环内部操作对象
Manipulating an object from inside a loop that borrows it
我正在用 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 行循环结束(我更愿意了解实际解决方案的样子)。
鉴于您不需要完整的 ServerReader
来处理消息,您可以使 process_message
成为一个自由函数,然后将 &mut self.counters
传递给它。然后你有 server
和 counters
的不相交借用,这很好。
或者如果 ServerReader
的非 server
部分较大,将其提取到它自己的结构中,并使 process_message
成为该结构的 impl 方法。
为了允许 Iterator
中的可变性,您应该使用 iter_mut()
并处理可变引用 (&mut message
)。然后,为了避免额外的借位,你可以只在循环体中执行加法:
for &mut message in self.server.iter_mut() {
println!("Received {}", message);
*self.counters.entry(message).or_insert(0) += 1;
}
根据您的计算,您不能在借用 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 编写一些连接到远程服务器的代码,并根据该服务器发送的消息计算一些统计数据或根据这些统计数据执行操作。但这对我来说更像是一个学习项目,我 运行 遇到了一个问题。
这是我为重现问题而减少到最低限度的代码:
// 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 行循环结束(我更愿意了解实际解决方案的样子)。
鉴于您不需要完整的 ServerReader
来处理消息,您可以使 process_message
成为一个自由函数,然后将 &mut self.counters
传递给它。然后你有 server
和 counters
的不相交借用,这很好。
或者如果 ServerReader
的非 server
部分较大,将其提取到它自己的结构中,并使 process_message
成为该结构的 impl 方法。
为了允许 Iterator
中的可变性,您应该使用 iter_mut()
并处理可变引用 (&mut message
)。然后,为了避免额外的借位,你可以只在循环体中执行加法:
for &mut message in self.server.iter_mut() {
println!("Received {}", message);
*self.counters.entry(message).or_insert(0) += 1;
}
根据您的计算,您不能在借用 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);
}