放弃不可变的借用以创建可变的借用。

4

我正在学习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请求可变借用时,会导致编译错误。
我认为我缺少一种允许我进行此类处理的模式或关键字。目前,我尝试在if作用域的开头使用drop,以及其他current_dist评估方式。
let current_dist;
{
    current_dist = dist.get(&state.node);
}

或者

let current_dist = {|| dist.get(&state.node)}();

但是不可变借用的作用域仍然发生在if语句之后。


(current_dist == None)|(state.cost < * current_dist.unwrap())我无法想象您想要进行位或操作。 - Shepmaster
请查看如何创建一个 [MCVE]。您没有定义 NodeStateheap 是什么。 - Shepmaster
1
我更正了 ||。通过创建 stateState 的定义似乎是暗示的,而 Nodeheap 似乎不相关(Node 是一组坐标,heap 是一个堆)。 - Thrastylon
然后从你的问题中删除它们,因为Nodeheap似乎不相关。我强烈建议您查看我们所说的[MCVE]并强调Minimal。这样做的原因有两个:1.通过缩小问题范围,您更有可能自己解决问题,2.缩小的问题更容易让其他人快速理解。这些人包括回答者和将来发现您问题的人。 - Shepmaster
1个回答

10

非词汇生命周期后

由于非词汇生命周期现在已经启用,原始代码可以编译。尽管如此,仍建议使用entry API来提高效率,否则必须多次哈希键:

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);
            }
        }
    }
}

非词法生命周期之前

因为dist.get触发了可变借用

不,它只是一个不可变借用

pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
    K: Borrow<Q>,
    Q: Hash + Eq, 
我尝试了一个drop

显式的drop并不影响生命周期。

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调用中不再隐式恐慌。

另请参阅:


我纠正了(im)mutable,那是一个打字错误。给出的指针正是我所需要的。 - Thrastylon
谢谢!我来这里想知道如何在HashMap上调用“.get()”,然后根据get()调用的值在同一个map上调用“.remove()”,而不会遇到“因为它也被作为不可变借用而无法作为可变借用”的错误。 - Gavin Ray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接