如何借用 HashMap 同时进行读写? [英] How can I borrow from a HashMap to read and write at the same time?

查看:43
本文介绍了如何借用 HashMap 同时进行读写?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个函数 f 接受两个引用,一个是 mut,一个不是 mut.我在 HashMap 中有 f 的值:

I have a function f that accepts two references, one mut and one not mut. I have values for f inside a HashMap:

use std::collections::HashMap;

fn f(a: &i32, b: &mut i32) {}

fn main() {
    let mut map = HashMap::new();

    map.insert("1", 1);
    map.insert("2", 2);

    {
        let a: &i32 = map.get("1").unwrap();
        println!("a: {}", a);

        let b: &mut i32 = map.get_mut("2").unwrap();
        println!("b: {}", b);
        *b = 5;
    }
    println!("Results: {:?}", map)
}

这不起作用,因为 HashMap::getHashMap::get_mut 试图同时可变借和不可变借:

This doesn't work because HashMap::get and HashMap::get_mut attempt to mutably borrow and immutably borrow at the same time:

error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
  --> src/main.rs:15:27
   |
12 |         let a: &i32 = map.get("1").unwrap();
   |                       --- immutable borrow occurs here
...
15 |         let b: &mut i32 = map.get_mut("2").unwrap();
   |                           ^^^ mutable borrow occurs here
...
18 |     }
   |     - immutable borrow ends here

在我的实际代码中,我使用了一个庞大而复杂的结构,而不是一个 i32,因此克隆它不是一个好主意.

In my real code I'm using a large, complex structure instead of a i32 so it is not a good idea to clone it.

事实上,我正在可变/不可变地借用两种不同的东西,例如:

In fact, I'm borrowing two different things mutably/immutably, like:

struct HashMap {
    a: i32,
    b: i32,
}
let mut map = HashMap { a: 1, b: 2 };
let a = &map.a;
let b = &mut map.b;

有什么办法可以向编译器解释这实际上是安全的代码吗?

Is there any way to explain to the compiler that this is actually safe code?

我看到如何用 iter_mut 解决具体情况:

I see how it possible to solve in the concrete case with iter_mut:

{
    let mut a: &i32 = unsafe { mem::uninitialized() };
    let mut b: &mut i32 = unsafe { mem::uninitialized() };
    for (k, mut v) in &mut map {
        match *k {
            "1" => {
                a = v;
            }
            "2" => {
                b = v;
            }
            _ => {}
        }
    }
    f(a, b);
}

但与 HashMap::get/get_mut

推荐答案

TL;DR:您需要更改 HashMap

在使用方法时,编译器检查方法的内部,或执行任何运行时模拟:它仅将其所有权/借用检查分析基于方法的签名.

When using a method, the compiler does not inspect the interior of a method, or perform any runtime simulation: it only bases its ownership/borrow-checking analysis on the signature of the method.

就您而言,这意味着:

  • 使用 get 将借用整个 HashMap 直到引用存在,
  • 使用 get_mut 将可变地借用整个 HashMap 直到引用存在.
  • using get will borrow the entire HashMap for as long as the reference lives,
  • using get_mut will mutably borrow the entire HashMap for as long as the reference lives.

因此,使用 HashMap 不可能同时获得 &V&mut V> 同时.

And therefore, it is not possible with a HashMap<K, V> to obtain both a &V and &mut V at the same time.

因此,解决方法是完全避免使用 &mut V.

The work-around, therefore, is to avoid the need for a &mut V entirely.

这可以通过使用 CellRefCell 来实现:

This can be accomplished by using Cell or RefCell:

  • 把你的 HashMap 变成 HashMap>,
  • 在这两种情况下都使用 get
  • 使用 borrow() 获取引用,使用 borrow_mut() 获取可变引用.
  • Turn your HashMap into HashMap<K, RefCell<V>>,
  • Use get in both cases,
  • Use borrow() to get a reference and borrow_mut() to get a mutable reference.
use std::{cell::RefCell, collections::HashMap};

fn main() {
    let mut map = HashMap::new();

    map.insert("1", RefCell::new(1));
    map.insert("2", RefCell::new(2));

    {
        let a = map.get("1").unwrap();
        println!("a: {}", a.borrow());

        let b = map.get("2").unwrap();
        println!("b: {}", b.borrow());
        *b.borrow_mut() = 5;
    }

    println!("Results: {:?}", map);
}

这将在您每次调用 borrow()borrow_mut() 时添加运行时检查,并且如果您尝试错误地使用它们(如果这两个键是相等的,与您的期望不同).

This will add a runtime check each time you call borrow() or borrow_mut(), and will panic if you ever attempt to use them incorrectly (if the two keys are equal, unlike your expectations).

至于使用字段:这是有效的,因为编译器可以推断每个字段的借用状态.

As for using fields: this works because the compiler can reason about borrowing status on a per-field basis.

这篇关于如何借用 HashMap 同时进行读写?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆