我还在学习Rust,当我尝试将Dikjstra作为培训项目的一部分时,我遇到了这种特殊的问题。首先我定义一个HashMap
:
let mut dist: HashMap<Node, usize> = HashMap::new();
后来:
let state = State { node: next_node.clone(), cost: cost + 1 };
let current_dist = dist.get(&state.node);
if (current_dist == None) || (state.cost < *current_dist.unwrap()) {
dist.insert(state.node.clone(), state.cost);
heap.push(state);
}
产生编译错误,因为dist.get
会触发一个不可变的借位,直到if ... {...}
语句之后,特别是当我dist.insert
时,它会一直保留在范围内,要求进行可变的借用。 / p>
我想我错过了一个允许我这种过程的模式或关键字。现在,我在drop
范围的开头和其他if
评估中尝试了current_dist
,例如
let current_dist;
{
current_dist = dist.get(&state.node);
}
或
let current_dist = {|| dist.get(&state.node)}();
但是不可变借用范围的结束仍然发生在if
语句之后。
答案 0 :(得分:5)
因为
dist.get
触发了可变借用
不,it's just an immutable borrow:
fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where K: Borrow<Q>,
Q: Hash + Eq
我尝试了
drop
Explicit drops do not affect lifetimes。 Rust需要学习非词汇生命期才能实现这一目标。
let current_dist; { current_dist = dist.get(&state.node); }
在这里,你不是在欺骗任何人。如果编译器对此感到困惑,那就不太好了。这个仍然借用HashMap
,只有一些额外的块散布在那里。
let current_dist = {|| dist.get(&state.node)}();
同样在这里。从闭包返回引用是仍然返回引用。你真的不能轻易欺骗编译器认为你对HashMap
的引用不存在。
相反,您需要使用块来约束借用存在的时间。最简单的转变类似于:
use std::collections::HashMap;
fn main() {
let mut dist: HashMap<u8, u8> = HashMap::new();
let do_it = {
let current_dist = dist.get(&42);
current_dist == None || true
};
if do_it {
dist.insert(42, 42);
}
}
这不是最漂亮的,但有些组合者可以清理它:
use std::collections::HashMap;
fn main() {
let mut dist: HashMap<u8, u8> = HashMap::new();
let cost = 21;
if dist.get(&42).map_or(true, |&val| val < cost) {
dist.insert(42, 42);
}
}
请注意,现在unwrap
来电不再有隐含的恐慌。
这不是最有效的,因为您必须多次散列密钥。相反,您可以使用entry API:
use std::collections::HashMap;
use std::collections::hash_map::Entry;
fn main() {
let mut dist: HashMap<u8, u8> = HashMap::new();
let cost = 21;
match dist.entry(42) {
Entry::Vacant(entry) => {
entry.insert(42);
}
Entry::Occupied(mut entry) => {
if *entry.get() < cost {
entry.insert(42);
}
}
}
}