我正在尝试在 Rust 中实现责任链设计模式:
pub trait Policeman<'a> {
fn set_next(&'a mut self, next: &'a Policeman<'a>);
}
pub struct Officer<'a> {
deduction: u8,
next: Option<&'a Policeman<'a>>,
}
impl<'a> Officer<'a> {
pub fn new(deduction: u8) -> Officer<'a> {
Officer {deduction, next: None}
}
}
impl<'a> Policeman<'a> for Officer<'a> {
fn set_next(&'a mut self, next: &'a Policeman<'a>) {
self.next = Some(next);
}
}
fn main() {
let vincent = Officer::new(8); // -+ vincent enters the scope
let mut john = Officer::new(5); // -+ john enters the scope
let mut martin = Officer::new(3); // -+ martin enters the scope
// |
john.set_next(&vincent); // |
martin.set_next(&john); // |
} // martin, john, vincent out of scope
这会产生错误消息:
error[E0597]: `john` does not live long enough
--> src\main.rs:29:1
|
27 | john.set_next(&vincent);
| ---- borrow occurs here
28 | martin.set_next(&john);
29 | }
| ^ `john` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
error[E0597]: `martin` does not live long enough
--> src\main.rs:29:1
|
28 | martin.set_next(&john);
| ------ borrow occurs here
29 | }
| ^ `martin` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
error[E0597]: `john` does not live long enough
--> src\main.rs:29:1
|
28 | martin.set_next(&john);
| ---- borrow occurs here
29 | }
| ^ `john` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
为什么
john
活得不够久?vincent
john
martin
john
指 vincent
( vincent
范围内) martin
指 john (john
范围内)martin
超出范围(john
仍在范围内)john
超出范围(vincent
仍在范围内)vincent
超出范围 我需要如何更改生命周期或代码才能在 Rust 中正确实现责任链模式?
最佳答案
详细说明
你的问题很有趣,当然很难直接理解为什么它不起作用。如果您了解编译器如何进行统一,它会很有帮助。我们将遍历编译器为找出类型而执行的所有步骤。
为了使它更容易一些,我们使用这个简化的例子:
let vincent = Officer::new(8);
let mut john = Officer::new(5);
john.set_next(&vincent);
这会导致相同的错误消息:
error[E0597]: `john` does not live long enough
--> src/main.rs:26:1
|
25 | john.set_next(&vincent);
| ---- borrow occurs here
26 | }
| ^ `john` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
首先,让我们以更明确、更明智的形式转换代码:
{ // start 'v
let vincent = Officer::new(8);
{ // start 'j
let mut john = Officer::new(5);
john.set_next(&vincent);
} // end 'j
} // end 'v
好的,现在我们准备看看编译器在想什么,一步一步:
{ // start 'v let vincent = Officer::new(8); // : Officer<'?arg_vincent>
Rust 还不知道生命周期参数,因此它只能在这里推导出不完整的类型。希望我们可以稍后填写详细信息!当编译器想要显示缺少的类型信息时,它会打印一个下划线(例如
Vec<_>
)。在这个例子中,我将缺失的信息写为 '?arg_vincent
.这样我们以后就可以引用了。{ // start 'j let mut john = Officer::new(5); // : Officer<'?arg_john>
和上面一样。
john.set_next(&vincent);
现在它变得有趣了!编译器有这个函数签名:
fn set_next(&'a mut self, next: &'a Policeman<'a>)
现在,编译器的工作是找到一个合适的生命周期
'a
满足一系列条件:&'a mut self
和 john
是 self
这里。所以'a
活不过 john
.换句话说:'j
活下去 'a
, 表示 'j: 'a
. next: &'a ...
和 next
是 vincent
, 所以(就像上面一样), 'a
活不过 vincent
. 'v
活下去 'a
=> 'v: 'a`。 'a
在 Policeman<'a>
指(尚未确定)生命周期参数'?arg_vincent
(因为那是我们作为参数传递的)。但是'?arg_vincent
尚未固定且完全无界。所以这不会对 'a
施加限制(与前两点不同)。相反,我们选择 'a
确定 '?arg_vincent
后来:'?arg_vincent := 'a
. 简而言之:
'j: 'a and
'v: 'a
那么,什么是最多活到约翰的一生 和 最多只要文森特?
'v
还不够,因为它的生命周期更长 john
. 'j
很好;它满足上述条件。所以一切都好吗?不!我们选择了终身
'a = 'j
现在。因此我们也知道'?arg_vincent = 'j
!所以全类型vincent
是 Officer<'j>
.这反过来告诉编译器 vincent
借用一生的东西j
.但是vincent
生命周期长于 'j
,所以它比它的借用还长!那很糟。这就是编译器提示的原因。整个事情真的很复杂,我想大多数人在阅读了我的解释后,和我阅读了大多数数学证明后的感觉完全一样:每一步都有意义,但结果并不直观。也许这会稍微改善情况:
自
set_next()
函数要求所有生命周期为 'a
,我们对程序中的所有生命周期都施加了很多限制。这很快导致限制的矛盾,就像这里发生的那样。我的小例子的快速修复
...是删除
'a
来自 self
范围:fn set_next(&mut self, next: &'a Policeman<'a>)
通过这样做,我们消除了不必要的限制。不幸的是,这还不足以编译整个示例。
更通用的解决方案
我对你提到的设计模式不是很熟悉,但从它的外观来看,在编译时跟踪所涉及的生命周期几乎是不可能的。因此我会使用
Rc
或 Arc
而不是引用。使用这些智能指针,您不需要注释生命周期,一切都“正常工作”。唯一的缺点:一点点运行成本。但是不可能告诉您最佳解决方案:这实际上取决于手头的问题。
关于design-patterns - 如何使用特征对象链实现责任链模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46743353/