我有一个函数f
,它接受两个引用,一个mut
,一个不mut
。我在f
:
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
试图同时借入和不可避免地借入:
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
,因此克隆它并不是一个好主意。
事实上,我可以不可变地/不可变地借用两件不同的东西,例如:
struct HashMap {
a: i32,
b: i32,
}
let mut map = HashMap { a: 1, b: 2 };
let a = &map.a;
let b = &mut map.b;
有没有办法向编译器解释这实际上是安全的代码?
我看到如何用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
答案 0 :(得分:4)
TL; DR:您需要更改HashMap
的类型
使用方法时,编译器不检查方法的内部,或执行任何运行时模拟:它只将其所有权/借用检查分析基于方法的签名。< / p>
在您的情况下,这意味着:
get
将借用整个HashMap
,get_mut
将可变地借用整个HashMap
。因此,HashMap<K, V>
无法同时同时获得&V
和&mut V
。
因此,解决方法是完全避免使用&mut V
。
这可以通过Cell
或RefCell
:
HashMap
变为HashMap<K, RefCell<V>>
,get
borrow()
获取引用,borrow_mut()
获取可变引用。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()
时都会添加运行时检查,如果您尝试错误地使用它们(如果两个键相同,与您的期望不同),则会发生混乱。< / p>
至于使用字段:这是有效的,因为编译器可以推断每个字段的借用状态。
答案 1 :(得分:1)
自提出问题以来,某些事情似乎已经改变。在Rust 1.38.0(可能更早)中,以下代码可以编译和运行:
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)
}
不需要RefCell
,甚至不需要内部作用域。