mirror of https://github.com/sunface/rust-course
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4.4 KiB
4.4 KiB
生命周期声明的范围过大
在大多时候,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> {
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);
}