Thr*_*lon 3 rust borrow-checker
我还在学习Rust,在尝试将Dikjstra作为培训项目的一部分时,我遇到了这种特殊的问题.首先我定义一个HashMap:
let mut dist: HashMap<Node, usize> = HashMap::new();
Run Code Online (Sandbox Code Playgroud)
然后:
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);
}
Run Code Online (Sandbox Code Playgroud)
这产生了一个编译错误,因为dist.get触发了一个不可变的借位,它在if ... {...}语句之后一直保留在范围内,特别是当我dist.insert要求一个可变的借位时.
我想我错过了一个模式或关键字,允许我这种类型的过程.现在我尝试了范围drop的开头if,以及其他current_dist评估
let current_dist;
{
current_dist = dist.get(&state.node);
}
Run Code Online (Sandbox Code Playgroud)
要么
let current_dist = {|| dist.get(&state.node)}();
Run Code Online (Sandbox Code Playgroud)
但是,在if声明之后,仍然会发生不可变借款的范围.
因为
dist.get触发了可变的借用
不,这只是一个不可变的借款:
use std::collections::hash_map::Entry;
use std::collections::HashMap;
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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我试过了
drop
明确的丢弃不会影响生命周期.Rust需要学习非词汇生命周期甚至可能.
Run Code Online (Sandbox Code Playgroud)pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Hash + Eq,
在这里,你不是在欺骗任何人.如果编译器对此感到困惑,那就不太好了.这仍然有借用HashMap,只有一些额外的块散落.
Run Code Online (Sandbox Code Playgroud)let current_dist; { current_dist = dist.get(&state.node); }
同样在这里.从闭包返回引用仍然返回引用.你真的不能轻易欺骗编译器认为你的引用HashMap不存在.
相反,您需要使用块来约束借用存在的时间.最简单的转换类似于:
let current_dist = {|| dist.get(&state.node)}();
Run Code Online (Sandbox Code Playgroud)
这不是最漂亮的,但有些组合器可以清理它:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,现在unwrap呼叫中不再存在隐含的恐慌.
这不是最有效的,因为您必须多次散列密钥.相反,您可以使用条目API:
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);
}
}
Run Code Online (Sandbox Code Playgroud)