首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么通过提取方法进行重构会触发借阅检查程序错误?

为什么通过提取方法进行重构会触发借阅检查程序错误?
EN

Stack Overflow用户
提问于 2019-07-13 09:12:54
回答 2查看 882关注 0票数 10

我的网络应用程序的体系结构可以简化为以下内容:

代码语言:javascript
复制
use std::collections::HashMap;

/// Represents remote user. Usually has fields,
/// but we omit them for the sake of example.
struct User;

impl User {
    /// Send data to remote user.
    fn send(&mut self, data: &str) {
        println!("Sending data to user: \"{}\"", data);
    }
}

/// A service that handles user data.
/// Usually has non-trivial internal state, but we omit it here.
struct UserHandler {
    users: HashMap<i32, User>,  // Maps user id to User objects.
    counter: i32  // Represents internal state
}

impl UserHandler {
    fn handle_data(&mut self, user_id: i32, data: &str) {
        if let Some(user) = self.users.get_mut(&user_id) {
            user.send("Message received!");
            self.counter += 1;
        }
    }
}

fn main() {
    // Initialize UserHandler:
    let mut users = HashMap::new();
    users.insert(1, User{});
    let mut handler = UserHandler{users, counter: 0};

    // Pretend we got message from network:
    let user_id = 1;
    let user_message = "Hello, world!";
    handler.handle_data(user_id, &user_message);
}

游乐场

这个还行。我想在UserHandler中创建一个单独的方法,当我们已经建立了具有给定id的用户时,它将处理用户输入。因此,它变成:

代码语言:javascript
复制
impl UserHandler {
    fn handle_data(&mut self, user_id: i32, data: &str) {
        if let Some(user) = self.users.get_mut(&user_id) {
            self.handle_user_data(user, data);
        }
    }

    fn handle_user_data(&mut self, user: &mut User, data: &str) {
        user.send("Message received!");
        self.counter += 1;
    }
}

游乐场

突然之间,它不编译了!

代码语言:javascript
复制
error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:24:13
   |
23 |         if let Some(user) = self.users.get_mut(&user_id) {
   |                             ---------- first mutable borrow occurs here
24 |             self.handle_user_data(user, data);
   |             ^^^^                  ---- first borrow later used here
   |             |
   |             second mutable borrow occurs here

乍一看,错误是非常明显的:您不能拥有对selfself属性的可变引用--这就像有两个对self的可变引用一样。但是,不管怎么说,我的do在原始代码中有两个可变的引用!

  1. 为什么这个简单的重构触发器借用检查器错误?
  2. 我如何绕过它,像这样分解UserHandler::handle_data方法呢?

如果您想知道我为什么想要这样的重构,那么考虑一种情况,当用户可以发送多种类型的消息时,所有这些消息都需要以不同的方式处理,但是有一个共同的部分:必须知道哪个User对象发送了这条消息。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-07-13 09:50:13

编译器阻止您两次借用HashMap是正确的。假设在handle_user_data()中,您也试图借用self.users。你会打破在铁锈里借债的规则,因为你已经有了一个可变的借款,而你只能借一个。

既然你不能为你的self借两次handle_user_data(),我会提出一个解决方案。我不知道这是不是最好的,但它的工作没有不安全和没有开销(我认为)。

其想法是使用一个中间结构,它将借用self的其他字段。

代码语言:javascript
复制
impl UserHandler {
    fn handle_data(&mut self, user_id: i32, data: &str) {
        if let Some(user) = self.users.get_mut(&user_id) {
            Middle::new(&mut self.counter).handle_user_data(user, data);
        }
    }
}

struct Middle<'a> {
    counter: &'a mut i32,
}

impl<'a> Middle<'a> {
    fn new(counter: &'a mut i32) -> Self {
        Self {
            counter
        }
    }

    fn handle_user_data(&mut self, user: &mut User, data: &str) {
        user.send("Message received!");
        *self.counter += 1;
    }
}

这样,编译器就知道我们不能借用users两次了。

如果您只需要借用一两件东西,那么一个快速的解决方案就是有一个相关的函数,该函数将它们作为参数:

代码语言:javascript
复制
impl UserHandler {
    fn handle_user_data(user: &mut User, data: &str, counter: &mut i32) {
        // ...
    }
}

我们可以改进这个设计:

代码语言:javascript
复制
struct UserHandler {
    users: HashMap<i32, User>, // Maps user id to User objects.
    middle: Middle,              // Represents internal state
}

impl UserHandler {
    fn handle_data(&mut self, user_id: i32, data: &str) {
        if let Some(user) = self.users.get_mut(&user_id) {
            self.middle.handle_user_data(user, data);
        }
    }
}

struct Middle {
    counter: i32,
}

impl Middle {
    fn new(counter: i32) -> Self {
        Self {
            counter
        }
    }

    fn handle_user_data(&mut self, user: &mut User, data: &str) {
        user.send("Message received!");
        self.counter += 1;
    }
}

现在,我们确信我们没有开销,语法也更加简洁。

更多信息可以在Niko的博客文章NLL之后:程序间冲突中找到。将此答案映射到博客文章:

  • 解决方案#1 ->“将结构视为通用但极端的解决方案”一节
  • 解决方案2 ->“作为通用的、但极端的解决方案的自由变量”部分(此处表示为关联函数)
  • 解决方案#3 ->“保理作为可能的修复”部分
票数 13
EN

Stack Overflow用户

发布于 2019-07-13 10:04:08

当调用self.handle_user_data时,您会不断地使用整个self,同时仍然有一个可变的借用的user对象,而user对象并不喜欢这个对象。你不能同时借两个可变的贷款。

避免这种情况的一种方法不是可变地使用整个self,而是可变地使用counter

代码语言:javascript
复制
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;
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57017747

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档