当前位置: 首页 > 知识库问答 >
问题:

从HashMap或Vec返回引用会导致借用持续时间超出其所处的范围吗?

吕俊美
2023-03-14

我有一个持续的编译错误,Rust抱怨说,当我试图可变地借用时,我有一个不可变的借用,但是不可变的借用来自另一个范围,我没有从它身上带来任何东西。

我有一些代码检查映射中的值,如果它存在,则返回它,否则它需要以各种方式改变映射。问题是我似乎找不到一种方法来除锈,让我两者都做吧,即使这两个操作是完全分开的。

下面是一些与我的代码结构相同的无意义代码,并显示了问题:

use std::collections::BTreeMap;

fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> {
    // extra scope in vain attempt to contain the borrow
    {
        // borrow immutably
        if let Some(key) = map.get(&key) {
            return Some(key);
        }
    }

    // now I'm DONE with the immutable borrow, but rustc still thinks it's borrowed

    map.insert(0, 0); // borrow mutably, which errors
    None
}

这错误输出:

error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
  --> src/lib.rs:14:5
   |
3  | fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> {
   |                  - let's call the lifetime of this reference `'1`
...
7  |         if let Some(key) = map.get(&key) {
   |                            --- immutable borrow occurs here
8  |             return Some(key);
   |                    --------- returning this value requires that `*map` is borrowed for `'1`
...
14 |     map.insert(0, 0); // borrow mutably, which errors
   |     ^^^^^^^^^^^^^^^^ mutable borrow occurs here

这对我来说没有任何意义。不可变的借入如何在那个范围内存活?!匹配的一个分支通过返回退出函数,另一个分支什么也不做,离开作用域。

我以前见过这种情况,在其他变量中,我错误地将借入从范围中走私出去,但这里不是这样!

诚然,借用是通过return语句逃出作用域的,但在函数中阻止进一步借用是荒谬的——程序不可能返回并继续运行!如果我在那里返回其他内容,错误就会消失,因此我认为这就是借阅检查器被挂断的原因。这对我来说像个虫子。

不幸的是,我一直找不到任何方法来重写它而不碰到相同的错误,所以如果是这样的话,这是一个特别讨厌的错误。

共有1个答案

罗建弼
2023-03-14

这是一个已知的问题,将在未来的非词汇生存期迭代中解决,但目前尚未在Rust 1.57中处理。

如果您插入到正在查找的同一个密钥,我建议您改用entry API。

现在你可以加上一点低效来解决这个问题。如果效率低下是不可接受的,那么还有更深层次的解决办法。

一般的想法是添加一个布尔值,告诉您是否存在值。此布尔值不挂起引用,因此不存在借用:

use std::collections::BTreeMap;

fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> {
    if map.contains_key(&key) {
        return map.get(&key);
    }

    map.insert(0, 0);
    None
}

fn main() {
    let mut map = BTreeMap::new();
    do_stuff(&mut map, 42);
    println!("{:?}", map)
}

类似的情况可以通过使用元素的索引而不是引用来解决。像上面的例子一样,由于需要再次检查切片边界,这可能会带来一点低效。

而不是

fn find_or_create_five<'a>(container: &'a mut Vec<u8>) -> &'a mut u8 {
    match container.iter_mut().find(|e| **e == 5) {
        Some(element) => element,
        None => {
            container.push(5);
            container.last_mut().unwrap()
        }
    }
}

你可以写:

fn find_or_create_five<'a>(container: &'a mut Vec<u8>) -> &'a mut u8 {
    let idx = container.iter().position(|&e| e == 5).unwrap_or_else(|| {
        container.push(5);
        container.len() - 1    
    });
    &mut container[idx]
}

这些类型的示例是NLL RFC中的主要案例之一:问题案例#3:跨函数的条件控制流。

不幸的是,这个特定的案例在Rust 1.57中还没有准备好。如果您选择在夜间使用实验性的-Zpolonius功能(RUSTFLAGS=“-Zpolonius”cargo nightly check),则这些原始示例都按原样编译:

use std::collections::BTreeMap;

fn do_stuff(map: &mut BTreeMap<i32, i32>, key: i32) -> Option<&i32> {
    if let Some(key) = map.get(&key) {
        return Some(key);
    }

    map.insert(0, 0);
    None
}
fn find_or_create_five(container: &mut Vec<u8>) -> &mut u8 {
    match container.iter_mut().find(|e| **e == 5) {
        Some(element) => element,
        None => {
            container.push(5);
            container.last_mut().unwrap()
        }
    }
}

另见:

>

  • 如何在Vec上更新或插入?

    这是同样的问题,但不返回引用,它与Rust 1.32中提供的NLL实现一起工作。

    即使NLL打开,循环中也会发生双重可变借用错误

    这个问题只是在稍微复杂一点的情况下。

    什么时候有必要绕过Rust的借货检查器?

    终极逃生舱。

  •  类似资料:
    • 我正在尝试将流转换为破折号。问题是我收到了很多错误,如下所示(虽然它似乎将其分割为多个文件,但我在播放器上也收到了错误)。问题是我为什么会收到这些错误以及如何修复它?如果我简单地将其“复制”为单个mp4文件,它就不会出错。 输出 复制到单个MP4工作正常ffmpeg-i“a.mkv”-y-映射0:1-c:0复制-f MP4-破折号1“out/out.MP4”-日志级别错误 探针 沙卡调试

    • 我正在努力抓住这个问题。我已经尝试了一切,但问题依然存在。基本上,我有一个随机数列表,当我试图比较循环中的值时,它抛出“indexer-ror:list-index-out-range” 我甚至尝试了范围(伦(谁)和伦(谁)。同样的事情。当把0而不是"当前技能"这是int变量它的工作。我不明白的是为什么比较两个值会抛出这个错误。这只是没有意义... 我不是在比较一个值,而是在比较索引本身吗???

    • 问题内容: 我有一个应用 分别提取数组的每个元素(通过索引) 然后将其绑定到可以利用单个元素的结构(查看和编辑) 但是每次数组减小大小时,都会导致索引超出范围错误,这不是直接由于我的代码而引起的 据我所知,这是因为:在用更改后的数组刷新循环之后,在某种程度上创建的视图还没有完全删除,仍然尝试访问超出范围的部分。但这就是我自己能想到的 这是我的示例代码: 有没有更好的方法可以满足上述两个要求而又不引

    • 我有一个简单的算法: 如果我删除第三个元素: 它工作得很好

    • 我有一个bean,我用它作为传输对象。bean的类定义为- 我正在使用这个bean,在带有Model属性注释的Spring控制器中。对于JSP,我有JSTL。我已经用这样的字段填充了JSP。 当我提交表单时,我得到Java . lang . indexoutofboundsexception:Index:0,Size: 0。

    • 我们有一个应用程序,我们在其中对REST API进行一些内部超文本传输协议调用来获取数据。但是有些请求花费的时间比预期的要长,所以我尝试增加超时持续时间。我尝试了以下操作: RequestConfig RequestConfig=RequestConfig.custom()。setConnectTimeout(30*1000)。build();HttpClient HttpClient=HttpC