Rust 为什么通过提取方法进行重构会触发借用检查器错误?
我的网络应用程序的体系结构可以简化为以下内容:Rust 为什么通过提取方法进行重构会触发借用检查器错误?,rust,borrow-checker,Rust,Borrow Checker,我的网络应用程序的体系结构可以简化为以下内容: 使用std::collections::HashMap; ///表示远程用户。通常有字段, ///但为了以身作则,我们省略了它们。 结构用户; impl用户{ ///向远程用户发送数据。 fn发送(&mut self,数据:&str){ println!(“向用户发送数据:\“{}\”,数据); } } ///处理用户数据的服务。 ///通常有非平凡的内部状态,但我们在这里省略它。 结构用户处理程序{ users:HashMap,//将用户id映射
使用std::collections::HashMap;
///表示远程用户。通常有字段,
///但为了以身作则,我们省略了它们。
结构用户;
impl用户{
///向远程用户发送数据。
fn发送(&mut self,数据:&str){
println!(“向用户发送数据:\“{}\”,数据);
}
}
///处理用户数据的服务。
///通常有非平凡的内部状态,但我们在这里省略它。
结构用户处理程序{
users:HashMap,//将用户id映射到用户对象。
计数器:i32//表示内部状态
}
impl用户处理程序{
fn handle_data(&mut self,user_id:i32,data:&str){
如果让某些(用户)=self.users.get_mut(&user_id){
发送(“消息已接收!”);
自计数器+=1;
}
}
}
fn main(){
//初始化用户处理程序:
让mut users=HashMap::new();
插入(1,用户{});
让mut handler=UserHandler{users,计数器:0};
//假设我们从网络上收到消息:
让用户_id=1;
让用户_message=“你好,世界!”;
handler.handle_数据(用户id和用户消息);
}
这样行。我想在UserHandler
中创建一个单独的方法,当我们已经确定具有给定id的用户存在时,该方法处理用户输入。因此,它变成:
impl UserHandler{
fn handle_data(&mut self,user_id:i32,data:&str){
如果让某些(用户)=self.users.get_mut(&user_id){
自行处理用户数据(用户,数据);
}
}
fn处理用户数据(&mut-self,用户:&mut-user,数据:&str){
发送(“消息已接收!”);
自计数器+=1;
}
}
突然,它无法编译
error[E0499]:不能一次多次借用“*self”作为可变项
-->src/main.rs:24:13
|
23 |如果让一些(用户)=self.users.get_mut(&user_id){
|------------第一个可变借用发生在此处
24 |自行处理|用户|数据(用户,数据);
|^^^^----先借后用
| |
|第二个可变借用发生在这里
乍一看,错误是非常明显的:您不能有一个对self
的可变引用和一个self
属性的可变引用-这就像有两个对self
的可变引用。但是,见鬼,我在原始代码中有两个这样的可变引用
UserHandler::handle\u data
方法如果你想知道为什么我想要这样的重构,考虑一个用户可以发送的多种类型消息的情况,都需要不同的处理,但是有一个共同的部分:必须知道哪个<代码>用户< /代码>对象发送了这个消息。
< p>编译器是正确的,以防止你借用<代码> HashMap <代码>两次。ose在handle\u user\u data()
中,您还尝试借用self.users
。您将打破Rust中的借用规则,因为您已经拥有可变借用,并且只能借用一个
既然你不能为你的处理用户数据()
借用self
两次,我将提出一个解决方案。我不知道它是否是最好的,但它工作起来没有不安全,也没有开销(我想)
这个想法是使用一个中间结构,它将借用self
的其他字段:
impl UserHandler{
fn handle_data(&mut self,user_id:i32,data:&str){
如果让某些(用户)=self.users.get_mut(&user_id){
中间::新建(&mut self.counter)。处理用户数据(用户,数据);
}
}
}
结构中间{
fn新(计数器:&'a mut i32)->自身{
自我{
柜台
}
}
fn处理用户数据(&mut-self,用户:&mut-user,数据:&str){
发送(“消息已接收!”);
*自计数器+=1;
}
}
这样,编译器就知道我们不能借用用户
两次
如果您只有一两件东西要借,一个快速的解决方案是使用一个关联函数,将它们作为参数:
impl UserHandler {
fn handle_user_data(user: &mut User, data: &str, counter: &mut i32) {
// ...
}
}
我们可以改进这种设计:
struct UserHandler{
users:HashMap,//将用户id映射到用户对象。
middle:middle,//表示内部状态
}
impl用户处理程序{
fn handle_data(&mut self,user_id:i32,data:&str){
如果让某些(用户)=self.users.get_mut(&user_id){
self.middle.handle\u user\u数据(user,data);
}
}
}
结构中间{
柜台:i32,
}
简单中间{
fn新(计数器:i32)->自身{
自我{
柜台
}
}
fn处理用户数据(&mut-self,用户:&mut-user,数据:&str){
发送(“消息已接收!”);
自计数器+=1;
}
}
现在,我们确信我们没有开销,语法也更清晰了
更多信息可在Niko Matsakis的博文中找到。将此答案映射到博文:
- 解决方案#1->“将结构视为一般但极端的解决方案”部分
- 解决方案#2->“自由变量作为一般但极端的解决方案”部分(此处表示为相关函数)
- 解决方案#3->“作为可能修复的因素”部分
HashMap
两次。假设在handle\u user\u data()
中,您也尝试借用self.users
。您将打破Rust中的借用规则,因为您已经拥有可变借用,并且只能拥有一个
既然你不能为你的处理用户数据()
借用self
两次,我将提出一个解决方案。我不知道这是否是最好的,但它与
impl UserHandler {
fn handle_data(&mut self, user_id: i32, data: &str) {
if let Some(user) = self.users.get_mut(&user_id) {
handle_user_data(user, data, &mut self.counter);
}
}
}
fn handle_user_data(user: &mut User, data: &str, counter: &mut i32) {
user.send(data);
*counter += 1;
}