kre*_*reo 4 rust borrow-checker
我的网络应用程序的体系结构可以简化为以下内容:
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);
}
Run Code Online (Sandbox Code Playgroud)
这样就可以了。UserHandler当我们已经建立具有给定id的用户时,我想创建一个单独的方法来处理用户输入。这样就变成了:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
突然之间,它无法编译!
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
Run Code Online (Sandbox Code Playgroud)
乍一看,该错误非常明显:您不能对self和属性进行self可变引用-就像对有两个可变引用一样self。但是,到底,我在原始代码中确实有两个这样的可变引用!
UserHandler::handle_data这种方法?如果您想知道为什么要进行这种重构,请考虑以下情况:用户可以发送多种类型的消息,所有消息都需要以不同的方式处理,但是有一个共同的部分:必须知道哪个User对象发送了此消息。
编译器可以防止您HashMap两次借用。假设handle_user_data()你也想借钱self.users。您将破坏Rust中的借用规则,因为您已经在其上进行了可变借用,并且只能拥有一个借用。
由于您不能self两次借用您handle_user_data(),我将提出一个解决方案。我不知道这是否是最好的,但是它可以在不安全和没有开销的情况下工作(我认为)。
这个想法是使用一个中间结构来借用以下其他字段self:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这样,编译器知道我们不能借users两次。
如果只借一两件事,一个快速的解决方案是拥有一个将它们作为参数的关联函数:
impl UserHandler {
fn handle_user_data(user: &mut User, data: &str, counter: &mut i32) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以改进此设计:
impl UserHandler {
fn handle_user_data(user: &mut User, data: &str, counter: &mut i32) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我们确定没有开销,并且语法更加简洁。
| 归档时间: |
|
| 查看次数: |
122 次 |
| 最近记录: |