如何借用 HashMap 同时进行读写? [英] How can I borrow from a HashMap to read and write at the same time?
问题描述
我有一个函数 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::get
和 HashMap::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 entireHashMap
for as long as the reference lives, - using
get_mut
will mutably borrow the entireHashMap
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.
这可以通过使用 Cell
或 RefCell
来实现:
This can be accomplished by using Cell
or RefCell
:
- 把你的
HashMap
变成HashMap
,> - 在这两种情况下都使用
get
, - 使用
borrow()
获取引用,使用borrow_mut()
获取可变引用.
- Turn your
HashMap
intoHashMap<K, RefCell<V>>
, - Use
get
in both cases, - Use
borrow()
to get a reference andborrow_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屋!