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