gpt4 book ai didi

rust - 从 HashMap 或 Vec 返回引用会导致借用持续超出其所在的范围吗?

转载 作者:行者123 更新时间:2023-12-03 11:31:41 24 4
gpt4 key购买 nike

我有一个持续的编译错误,其中 Rust 提示我在尝试可变借用时有一个不可变借用,但不可变借用来自另一个范围,我没有从中带来任何东西。

我有一些代码可以检查 map 中的值,如果它存在,则返回它,否则它需要以各种方式改变 map 。问题是我似乎无法找到一种方法让 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

这对我来说没有任何意义。不可变借用如何超过该范围?!一个分支 match通过 return 退出函数,而另一个什么都不做并离开范围。

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

确实,借用正在通过 return 转义范围语句,但它阻止了函数中更远的借用是荒谬的——程序不可能返回并继续运行!如果我在那里返回其他东西,错误就会消失,所以我认为这就是借用检查器被挂断的原因。这对我来说就像一个错误。

不幸的是,我一直无法找到任何方法来重写它而不会遇到相同的错误,因此如果是这种情况,这是一个特别讨厌的错误。

最佳答案

这是一个 known issue这将通过 non-lexical lifetimes 的 future 迭代来解决,但是是 当前未处理 从 Rust 1.51 开始。
如果您要插入与查找相同的 key ,我建议您使用 use the entry API反而。
您现在可以添加一些低效率来解决此问题。如果效率低下是 Not Acceptable ,there are deeper workarounds .HashMap一般的想法是添加一个 bool 值,告诉您一个值是否存在。这个 bool 值不卡在引用上,所以没有借用:

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)
}
Vec类似的情况可以通过使用元素的索引而不是引用来解决。与上面的情况一样,由于需要再次检查切片边界,这可能会导致效率低下。
代替
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]
}
非词法生命周期
这些类型的示例是 the NLL RFC 中的主要案例之一。 : Problem case #3: conditional control flow across functions .
不幸的是,这个特定案例在 Rust 1.51 中还没有准备好。如果您选择加入实验 -Zpolonius在 nightly 中,这些原始示例中的每一个都按原样编译:
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()
}
}
}
也可以看看:
  • How to update-or-insert on a Vec?
    这是相同的问题,不返回引用,它与 Rust 1.32 中可用的 NLL 的实现一起工作。
  • Double mutable borrow error in a loop happens even with NLL on
    这个问题不过是在稍微复杂一点的情况下。
  • When is it necessary to circumvent Rust's borrow checker?
    终极逃生舱口。
  • 关于rust - 从 HashMap 或 Vec 返回引用会导致借用持续超出其所在的范围吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60532287/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com