我正在尝试在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
好的,现在我们准备逐步了解编译器的想法:
Rust尚不知道生命周期参数,因此在这里只能推断出不完整的类型。希望我们以后可以填写详细信息!当编译器想要显示缺少的类型信息时,它会打印一个下划线(例如
Vec<_>
)。在此示例中,我将缺少的信息写为'?arg_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
那么,一生中最多能和john 和一样长,与Vincent一样长的生命是什么?
'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/