使用lazy_static时Rust出现`tree`可变多次借用错误的问题
Rust静态变量中可变借用与生命周期问题
测试代码(需执行cargo add lazy_static添加依赖)
#[macro_use] extern crate lazy_static; extern crate alloc; use alloc::collections::BTreeMap; use alloc::vec::Vec; fn get_vec<'a>(tree: &'a mut BTreeMap<&'a str, Vec<&'a u8>>, key: &'a str) -> &'a mut Vec<&'a u8> { if !tree.contains_key(key) { let v = Vec::new(); tree.insert(key, v); } tree.get_mut(key).unwrap() } lazy_static! { static ref VALUES: [u8; 2] = [1, 2]; static ref TEST_HASH: BTreeMap<&'static str, Vec<&'static u8>> = { let mut tree: BTreeMap<&str, Vec<&u8>> = BTreeMap::new(); let key = "test_key"; for value in VALUES.iter() { /* { // #1 Block: This work fine if !tree.contains_key(key) { let v = Vec::new(); tree.insert(key, v); } let v = tree.get_mut(key).unwrap(); v.push(&value); } */ { // #2 Block: This is not work let v = get_vec(&mut tree, key); v.push(&value); } } tree }; } fn main() { for (key, v) in &*TEST_HASH { println!("{} {:?}", key, v); } }
编译错误输出
error[E0499]: cannot borrow `tree` as mutable more than once at a time --> src/main.rs:34:33 | 34 | let v = get_vec(&mut tree, key); | ^^^^^^^^^ `tree` was mutably borrowed here in the previous iteration of the loop ... 38 | tree | ---- returning this value requires that `tree` is borrowed for `'static` error[E0505]: cannot move out of `tree` because it is borrowed --> src/main.rs:38:9 | 19 | let mut tree: BTreeMap<&str, Vec<&u8>> = BTreeMap::new(); | -------- binding `tree` declared here ... 34 | let v = get_vec(&mut tree, key); | --------- borrow of `tree` occurs here ... 38 | tree | ^^^^ | | | move out of `tree` occurs here | returning this value requires that `tree` is borrowed for `'static` Some errors have detailed explanations: E0499, E0505. For more information about an error, try `rustc --explain E0499`.
问题
- 该错误的问题根源是什么?
- #Block1与#Block2逻辑类似,为何前者可正常运行,后者报错?
解答
1. 错误根源
问题出在get_vec函数的生命周期注解过度绑定:
函数签名fn get_vec<'a>(tree: &'a mut BTreeMap<&'a str, Vec<&'a u8>>, key: &'a str) -> &'a mut Vec<&'a u8>把所有输入输出的生命周期都强制绑定为同一个'a。
结合静态变量场景来看:
TEST_HASH是'static生命周期的静态变量,意味着初始化时创建的tree最终要升级为'static生命周期。- 循环中调用
get_vec(&mut tree, key)时,编译器会推断:返回的Vec引用生命周期是'a,而'a又和tree的可变引用生命周期绑定,这个借用必须持续到tree被返回作为静态变量的时刻。 - 这导致每次循环的可变借用无法在当前块结束后释放,进而造成循环中多次可变借用冲突,同时返回
tree时它还处于被借用状态,触发E0499和E0505错误。
2. Block1与Block2的差异
Block1能正常运行的原因
Block1中所有操作都在同一个作用域内直接操作tree,编译器可以进行局部生命周期分析:
- 先调用
tree.contains_key(key)产生的不可变借用,在调用tree.get_mut(key)之前就已释放。 tree.get_mut(key)返回的可变引用,生命周期仅限于当前循环块——块结束时v被销毁,可变借用随之释放,不会影响下一次循环,也不会延长到tree被返回的时刻。
Block2报错的原因
Block2通过函数封装后,函数的生命周期注解限制了编译器的分析粒度:
- 函数签名强制将
tree的可变引用、key、返回的Vec引用三者生命周期绑定,编译器无法得知返回的Vec引用会在循环块内被销毁,只能保守地认为借用会持续到tree的整个生命周期(直到作为静态变量返回)。 - 这种保守推断导致循环中每次调用
get_vec都会产生一个持续到tree返回时刻的可变借用,造成多次借用冲突,同时返回tree时它还处于被借用状态,最终触发错误。
内容的提问来源于stack exchange,提问作者Jihwan Park
相关产品推荐
相关产品推荐

