简体   繁体   English

Rust 无法将不同的结构插入 hashmap

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM