Bil*_*ser 16 rust borrow-checker
我有一个持久的编译错误,其中Rust抱怨我在尝试可变借用时有一个不可变的借位,但是不可变借用来自另一个范围,而我并没有从中带来任何东西.
我有一些代码检查地图中的值,如果它存在,则返回它,否则它需要以各种方式改变地图.问题是我似乎无法找到让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
}
Run Code Online (Sandbox Code Playgroud)
这出错了:
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
Run Code Online (Sandbox Code Playgroud)
这对我没有任何意义.不可变借用如何比这个范围更长?!其中一个分支match通过该函数退出,另一个分支return不执行任何操作并离开范围.
我已经看到过这种情况发生之前,我错误地将借用从其他变量的范围走私出来,但事实并非如此!
确实,借用是通过return声明来逃避范围,但是阻止在函数中进一步向下借用是荒谬的 - 程序不可能返回并继续前进!如果我在那里返回其他东西,那么错误就会消失,所以我认为这是借用检查器被挂起的东西.这对我来说就像一个错误.
不幸的是,我没有找到任何方法来重写这个而不会遇到同样的错误,所以如果是这样的话,这是一个特别讨厌的错误.
She*_*ter 14
这是一个已知的问题,将由非词汇生命周期解决,这本身就是基于MIR.如果您正在插入您正在查找的相同密钥,我建议您使用条目API.
你可以添加一个效率低下的smidgen来解决这个问题.
HashMap一般的想法是添加一个布尔值,告诉您是否存在值.这个布尔值不会挂起引用,所以没有借用:
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)
}
Run Code Online (Sandbox Code Playgroud)
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()
}
}
}
Run Code Online (Sandbox Code Playgroud)
你可以写:
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]
}
Run Code Online (Sandbox Code Playgroud)
这些类型的示例是NLL RFC中的主要案例之一:问题案例#3:跨函数的条件控制流.
不幸的是,从Rust 1.32开始,这个具体案例还没有准备好.如果您-Zpolonius在夜间选择使用实验性功能,则每个原始示例都按原样编译:
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
}
Run Code Online (Sandbox Code Playgroud)
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()
}
}
}
Run Code Online (Sandbox Code Playgroud)
也可以看看:
没有返回引用也是同样的问题,这与Rust 1.32中可用的NLL的实现一起工作.
这个问题但是在一个稍微复杂的情况下.
终极逃生舱.
| 归档时间: |
|
| 查看次数: |
1836 次 |
| 最近记录: |