[英]Rust can't insert different structs into hashmap
Suppose I have a struct and I want to hash it into 2 HashMap
s, such that the first one holds a reference to it and the second one owns it, like that:假设我有一个结构,我想将 hash 放入 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(())
}
This works fine and as expected.这可以正常工作并且符合预期。 But, when I try to insert a second person, the borrow checker freaks out:
但是,当我尝试插入第二个人时,借阅检查器吓坏了:
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);
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(())
}
The error I'm getting is:我得到的错误是:
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
But line 26 has nothing to do with person1
so why does this happen?但是第 26 行与
person1
无关,为什么会这样呢?
When you move person1
into first_name_table
you invalidate the reference &person1
stored in last_name_table
but if you never use last_name_table
again then the compiler lets the code compile, but as soon as you attempt to use last_name_table
the compile will throw an error because it contains an invalid reference.当您将
person1
移动到first_name_table
时,会使存储在last_name_table
中的引用&person1
无效,但是如果您不再使用last_name_table
则编译器会允许代码编译,但是一旦您尝试使用last_name_table
,编译就会抛出错误,因为它包含无效的引用. It doesn't matter how or when you try to use it.尝试使用它的方式或时间都没有关系。 Even simply dropping it will also trigger the error:
即使只是简单地删除它也会触发错误:
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(())
}
I'm surprised the first version compiles at all, considering you store a reference to the person and then immediately move it.考虑到您存储对该人的引用然后立即移动它,我对第一个版本完全编译感到惊讶。 I guess the compiler can see that the reference isn't used beyond that point ( edit: this is because of
#[may_dangle]
within the map's Drop
implementation).我猜编译器可以看到在该点之后没有使用引用(编辑:这是因为地图的
Drop
实现中的#[may_dangle]
)。 Regardless, its not quite the second insert but any use of last_name_table
will trigger an error :无论如何,它不是第二次插入,但任何使用
last_name_table
都会触发错误:
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
You could try to insert it into one then get the reference to avoid the move issue:您可以尝试将其插入其中,然后获取参考以避免移动问题:
let person1_ref = first_name_table.entry(first_name1).or_insert(person1);
last_name_table.insert(last_name1, person1_ref);
But that won't let you modify first_name_table
any more since last_name_table
is immutably referencing it.但这不会让您再修改
first_name_table
,因为last_name_table
是不可变地引用它。 Almost any operation on hash maps could end up moving existing elements meaning the reference would become invalid.几乎对 hash 地图的任何操作都可能最终移动现有元素,这意味着引用将变得无效。 Rust will prevent you from doing this.
Rust 会阻止你这样做。
The fix is to clear up your ownership model.解决方法是清除您的所有权 model。 I'd recommend using
Rc
such that each map shares ownership of the person.我建议使用
Rc
以便每个 map共享此人的所有权。 See it working on the 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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.