问题描述
假设我有一个结构体,我想将它散列到 2 个 HashMap
中,这样第一个拥有对它的引用,第二个拥有它,就像这样:
struct Person { id: i32 }
fn main() -> std::io::Result<()> {
let mut first_name_table = HashMap::new();
let mut last_name_table = HashMap::new();
let person1 = Person { id: 1};
let first_name1 = "first1";
let last_name1 = "last1";
last_name_table.insert(last_name1,&person1);
first_name_table.insert(first_name1,person1);
Ok(())
}
这工作正常并且符合预期。但是,当我尝试插入第二个人时,借用检查器吓坏了:
struct Person { id: i32 }
fn main() -> std::io::Result<()> {
let mut first_name_table = HashMap::new();
let mut last_name_table = HashMap::new();
let person1 = Person { id: 1};
let first_name1 = "first1";
let last_name1 = "last1";
last_name_table.insert(last_name1,person1);
let person2 = Person { id: 2};
let first_name2 = "first2";
let last_name2 = "last2";
last_name_table.insert(last_name2,&person2);
first_name_table.insert(first_name2,person2);
Ok(())
}
我得到的错误是:
error[E0505]: cannot move out of `person1` because it is borrowed
--> src/main.rs:20:42
|
19 | last_name_table.insert(last_name1,&person1);
| -------- borrow of `person1` occurs here
20 | first_name_table.insert(first_name1,person1);
| ^^^^^^^ move out of `person1` occurs here
...
26 | last_name_table.insert(last_name2,&person2);
| --------------- borrow later used here
但第 26 行与 person1
无关,为什么会发生这种情况?
解决方法
当您将 person1
移入 first_name_table
时,您会使存储在 &person1
中的引用 last_name_table
无效,但如果您不再使用 last_name_table
,那么编译器会让代码编译,但是一旦您尝试使用 last_name_table
,编译就会抛出错误,因为它包含无效的引用。您如何或何时尝试使用它并不重要。即使只是简单地删除它也会触发错误:
use std::collections::HashMap;
struct Person { id: i32 }
fn main() -> std::io::Result<()> {
let mut first_name_table = HashMap::new();
let mut last_name_table = HashMap::new();
let person1 = Person { id: 1};
let first_name1 = "first1";
let last_name1 = "last1";
last_name_table.insert(last_name1,&person1);
first_name_table.insert(first_name1,person1);
drop(last_name_table); // triggers error
Ok(())
}
,
我很惊讶第一个版本竟然可以编译,因为您存储了对这个人的引用,然后立即移动了它。我猜编译器可以看到在该点之后没有使用引用(编辑:这是因为地图的 #[may_dangle]
实现中的 Drop
)。无论如何,这不是第二次插入,但任何使用 last_name_table
都会trigger an error:
let mut first_name_table = HashMap::new();
let mut last_name_table = HashMap::new();
let person1 = Person { id: 1};
let first_name1 = "first1";
let last_name1 = "last1";
last_name_table.insert(last_name1,&person1);
first_name_table.insert(first_name1,person1);
println!("{:?}",last_name_table);
error[E0505]: cannot move out of `person1` because it is borrowed
--> src/main.rs:15:42
|
14 | last_name_table.insert(last_name1,&person1);
| -------- borrow of `person1` occurs here
15 | first_name_table.insert(first_name1,person1);
| ^^^^^^^ move out of `person1` occurs here
16 |
17 | println!("{:?}",last_name_table);
| --------------- borrow later used here
您可以尝试将其插入一个然后获取引用以避免移动问题:
let person1_ref = first_name_table.entry(first_name1).or_insert(person1);
last_name_table.insert(last_name1,person1_ref);
但这不会让您再修改 first_name_table
,因为 last_name_table
一直在引用它。几乎所有对哈希映射的操作都可能最终移动现有元素,这意味着引用将变得无效。 Rust 会阻止你这样做。
解决方法是清除您的所有权模型。我建议使用 Rc
,这样每个地图共享该人的所有权。看看它在 playground 上工作:
let mut first_name_table = HashMap::new();
let mut last_name_table = HashMap::new();
let person1 = Rc::new(Person { id: 1});
let first_name1 = "first1";
let last_name1 = "last1";
last_name_table.insert(last_name1,Rc::clone(&person1));
first_name_table.insert(first_name1,person1);
let person2 = Rc::new(Person { id: 2});
let first_name2 = "first2";
let last_name2 = "last2";
last_name_table.insert(last_name2,Rc::clone(&person2));
first_name_table.insert(first_name2,person2);