Rust闭包引用所属对象引发可变引用重复借用问题求助
Rust模拟Socket时的可变引用重复借用问题
我写了一段模拟Socket的代码,希望收到数据时通过传入的闭包定义回写逻辑,但编译失败,报错cannot borrow *self as mutable more than once at a time。
代码如下:
pub struct Socket { on_message_received: Box<dyn FnMut(&Vec<u8>, &mut Socket)>, } impl Socket { pub fn new(on_message_received: Box<dyn FnMut(&Vec<u8>, &mut Socket)>) -> Self { Self { on_message_received, } } pub fn read(&mut self, fake_data: &Vec<u8>) { (self.on_message_received)(fake_data, self); } pub fn write(&mut self, data: Vec<u8>) { println!( "Pretend this requires a mutable reference to self {:?}", data ); } } fn main() { let mut socket = Socket::new(Box::new(|data, socket| { socket.write(data.clone()); })); socket.read(&vec![1, 2, 3]); }
错误出在(self.on_message_received)(fake_data, self);这一行:调用闭包时已经可变借用了self.on_message_received字段,同时又将整个self作为可变引用传入闭包,导致同一时间存在两个对self的可变借用,违反了Rust的借用规则。我还尝试过通过setter方法定义闭包,让闭包捕获Socket引用,但依然遇到相同的冲突问题。
解决方案1:临时取出闭包避免同时借用
核心思路是在调用闭包前,先将闭包从Socket实例中临时取出,调用完成后再放回。这样调用闭包时,self的可变引用不会和闭包的借用产生冲突,因为闭包此时已不在self的字段中。
修改后的代码:
pub struct Socket { // 将闭包装在Option中,方便临时取出和放回 on_message_received: Option<Box<dyn FnMut(&Vec<u8>, &mut Socket)>>, } impl Socket { pub fn new(on_message_received: Box<dyn FnMut(&Vec<u8>, &mut Socket)>) -> Self { Self { on_message_received: Some(on_message_received), } } pub fn read(&mut self, fake_data: &Vec<u8>) { // 取出闭包,此时self.on_message_received变为None let mut callback = self.on_message_received.take().unwrap(); // 调用闭包,传入self,此时self的on_message_received字段为空,不会产生借用冲突 callback(fake_data, self); // 将闭包放回Socket实例 self.on_message_received = Some(callback); } pub fn write(&mut self, data: Vec<u8>) { println!( "Pretend this requires a mutable reference to self {:?}", data ); } } fn main() { let mut socket = Socket::new(Box::new(|data, socket| { socket.write(data.clone()); })); socket.read(&vec![1, 2, 3]); }
解决方案2:拆分Socket职责
如果业务允许,可以将Socket的消息处理逻辑和写操作职责拆分,单独定义SocketWriter结构体处理写操作,避免闭包直接持有整个Socket的可变引用:
pub struct Socket { on_message_received: Box<dyn FnMut(&Vec<u8>, &mut SocketWriter)>, } // 专门处理写操作的结构体 pub struct SocketWriter; impl SocketWriter { pub fn write(&mut self, data: Vec<u8>) { println!( "Pretend this requires a mutable reference to self {:?}", data ); } } impl Socket { pub fn new(on_message_received: Box<dyn FnMut(&Vec<u8>, &mut SocketWriter)>) -> Self { Self { on_message_received, } } pub fn read(&mut self, fake_data: &Vec<u8>) { let mut writer = SocketWriter; (self.on_message_received)(fake_data, &mut writer); } } fn main() { let mut socket = Socket::new(Box::new(|data, writer| { writer.write(data.clone()); })); socket.read(&vec![1, 2, 3]); }
这种方式通过职责拆分缩小了可变引用的范围,从根源上避免了借用冲突。
内容的提问来源于stack exchange,提问作者qnilab
相关产品推荐
相关产品推荐

