知乎上一篇2018年初关于nll的文章,当时nll还未进stable。大致意思是在nll情况下这个代码可以通过编译,以前看的时候没有测试过,今天试了一下是编译不过的,应该如何更正一下?
use std::collections::HashMap;
fn main() {
let map = &mut HashMap::new();
map.insert(22, format!("Hello, world"));
map.insert(44, format!("Goodbye, world"));
assert_eq!(&*get_default(map, 22), "Hello, world");
assert_eq!(&*get_default(map, 66), "");
}
fn get_default(map: &mut HashMap<usize, String>, key: usize) -> &mut String {
match map.get_mut(&key) {
Some(value) => value,
None => {
map.insert(key, "".to_string());
map.get_mut(&key).unwrap()
}
}
}
报错如下
error[E0499]: cannot borrow `*map` as mutable more than once at a time
--> examples\nll_test.rs:38:13
|
34 | fn get_default(map: &mut HashMap<usize, String>, key: usize) -> &mut String {
| - let's call the lifetime of this reference `'1`
35 | match map.get_mut(&key) {
| - --- first mutable borrow occurs here
| _____|
| |
36 | | Some(value) => value,
37 | | None => {
38 | | map.insert(key, "".to_string());
| | ^^^ second mutable borrow occurs here
39 | | map.get_mut(&key).unwrap()
40 | | }
41 | | }
| |_____- returning this value requires that `*map` is borrowed for `'1`
Ext Link: https://zhuanlan.zhihu.com/p/32884290
1
共 5 条评论, 1 页
评论区
写评论我重新思考了一下,觉得我之前应该说错了,这段代码应该不是双重可变引用的问题。但是貌似应该不是nll能解决的。应该是当前borrow checker设计上就不能解决的问题,需要下一代borrow checker。
这个问题应该和这个 issue#47680
https://github.com/rust-lang/rust/issues/47680
是一样的,在http://smallcultfollowing.com/babysteps/blog/2018/04/27/an-alias-based-formulation-of-the-borrow-checker/
这篇文章中提到下一代borrow checker代号polonius也许才能解决。Playground
我猜这里是因为
Some(value) => value,
这个分支用到了map.get_mut(&key)
的值,导致对*map
的可变引用无法在进入分支之前结束,把返回value
这个去掉就可以通过编译了。谢谢!
--
👇
xjkdev: 我觉得文章的例子应该也是举错了。
我觉得文章的例子应该也是举错了。
我觉得这不是nll的问题,nll解决的是能通过手动加花括号就能解决的生命周期问题,而你这个不是nll能解决的声明周期问题。
我觉得问题是因为你参数先&mut借用了map,但是结尾又返回了从&mut map里派生的&mut String。在调用者的位置,就相当于&mut map这个对象同时有两个可变引用了,这个是lifetime规则不允许的——一个对象同时存在两个可变引用。
get_default这样的功能可以用HashMap的entry机制来做。