生命周期声明的范围过大
在大多时候,Rust 的生命周期你只要标识了,即可以通过编译,但是总是存在一些情况,会导致编译无法通过,本文就讲述这样一种情况:因为生命周期声明的范围过大,导致了编译无法通过,希望大家喜欢
例子 1
struct Interface<'a> { manager: &'a mut Manager<'a> } impl<'a> Interface<'a> { pub fn noop(self) { println!("interface consumed"); } } struct Manager<'a> { text: &'a str } struct List<'a> { manager: Manager<'a>, } impl<'a> List<'a> { pub fn get_interface(&'a mut self) -> Interface { Interface { manager: &mut self.manager } } } fn main() { let mut list = List { manager: Manager { text: "hello" } }; list.get_interface().noop(); println!("Interface should be dropped here and the borrow released"); // this fails because inmutable/mutable borrow // but Interface should be already dropped here and the borrow released use_list(&list); } fn use_list(list: &List) { println!("{}", list.manager.text); }
运行后报错:
error[E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable // `list`无法被借用,因为已经被可变借用
--> src/main.rs:40:14
|
34 | list.get_interface().noop();
| ---- mutable borrow occurs here // 可变借用发生在这里
...
40 | use_list(&list);
| ^^^^^
| |
| immutable borrow occurs here // 新的不可变借用发生在这
| mutable borrow later used here // 可变借用在这里结束
这段代码看上去并不复杂,实际上难度挺高的,首先在直觉上,list.get_interface()
借用的可变引用,按理来说应该在这行代码结束后,就归还了,为何能持续到use_list(&list)
后面呢?
这是因为我们在get_interface
方法中声明的lifetime
有问题,该方法的参数的生明周期是'a
,而List
的生命周期也是'a
,说明该方法至少活得跟List
一样久,再回到main
函数中,list
可以活到main
函数的结束,因此list.get_interface()
借用的可变引用也会活到main
函数的结束,在此期间,自然无法再进行借用了。
要解决这个问题,我们需要为get_interface
方法的参数给予一个不同于List<'a>
的生命周期'b
,最终代码如下:
struct Interface<'b, 'a: 'b> { manager: &'b mut Manager<'a> } impl<'b, 'a: 'b> Interface<'b, 'a> { pub fn noop(self) { println!("interface consumed"); } } struct Manager<'a> { text: &'a str } struct List<'a> { manager: Manager<'a>, } impl<'a> List<'a> { pub fn get_interface<'b>(&'b mut self) -> Interface<'b, 'a> where 'a: 'b { Interface { manager: &mut self.manager } } } fn main() { let mut list = List { manager: Manager { text: "hello" } }; list.get_interface().noop(); println!("Interface should be dropped here and the borrow released"); // this fails because inmutable/mutable borrow // but Interface should be already dropped here and the borrow released use_list(&list); } fn use_list(list: &List) { println!("{}", list.manager.text); }
当然,咱还可以给生命周期给予更有意义的名称:
struct Interface<'text, 'manager> { manager: &'manager mut Manager<'text> } impl<'text, 'manager> Interface<'text, 'manager> { pub fn noop(self) { println!("interface consumed"); } } struct Manager<'text> { text: &'text str } struct List<'text> { manager: Manager<'text>, } impl<'text> List<'text> { pub fn get_interface<'manager>(&'manager mut self) -> Interface<'text, 'manager> where 'text: 'manager { Interface { manager: &mut self.manager } } } fn main() { let mut list = List { manager: Manager { text: "hello" } }; list.get_interface().noop(); println!("Interface should be dropped here and the borrow released"); // this fails because inmutable/mutable borrow // but Interface should be already dropped here and the borrow released use_list(&list); } fn use_list(list: &List) { println!("{}", list.manager.text); }