【问题标题】:Drop a immutable borrow to make a mutable borrow删除不可变借用以进行可变借用
【发布时间】:2017-02-06 19:09:06
【问题描述】:

我仍在学习 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 &lt; *current_dist.unwrap()) 我无法想象你想要一个按位或操作。
  • 请查看如何创建minimal reproducible example。您尚未定义 NodeStateheap 是什么。
  • 我更正了||State 的定义似乎隐含在 state 的创建中,而 Nodeheap 似乎并不相关(Node 是一组坐标,heap 是一个堆)。
  • Nodeheap 似乎不相关 - 然后从您的问题中删除它们。再次,我强烈建议您查看minimal reproducible example 的含义;强调Minimal。这样做的原因有两个:1. 通过减少问题,您更有可能自己解决问题,2. 减少的问题更容易让 其他 人快速理解。这些人包括回答者找到您问题的未来提问者。

标签: rust borrow-checker


【解决方案1】:

在非词汇生命周期之后

由于现在启用了non-lexical lifetimes,因此可以编译原始代码。话虽如此,您仍然应该使用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 触发了可变借用

不,it's just an immutable borrow

pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
    K: Borrow<Q>,
    Q: Hash + Eq, 

我试过drop

Explicit drops do not affect lifetimes

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,这是一个错字。给出的指针正是我所需要的。
猜你喜欢
  • 2018-04-15
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 2019-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-12
相关资源
最近更新 更多