如何使用特征对象链来实现责任链模式?

How do I implement the Chain of Responsibility pattern using a chain of trait objects?

我正在尝试在 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活得不够长?

  1. 已创建vincent
  2. 已创建john
  3. 已创建martin
  4. john指的是vincent(范围内的vincent
  5. martin 指的是范围内的 john (john)
  6. martin 超出范围(john 仍在范围内)
  7. john 超出范围(vincent 仍在范围内)
  8. 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 selfjohnself。所以 'a 不可能比 john 长寿。换句话说:'joutlives'a,表示为'j: 'a.
  • 我们有 next: &'a ...nextvincent,所以(就像上面一样),'a 不能比 vincent 长寿。 'v 'a => 'v: 'a`.
  • 最后,Policeman<'a> 中的 '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,我们在程序中对所有生命周期施加了很多限制。这很快就会导致限制中的矛盾,就像这里发生的那样。

我的小例子的快速修复

...就是去掉self参数中的'a

fn set_next(&mut self, next: &'a Policeman<'a>)

这样我们就删除了不必要的限制。不幸的是,这还不足以编译整个示例。

更通用的解决方案

我不是很熟悉你提到的设计模式,但从它的外观来看,在编译时跟踪所涉及的生命周期几乎是不可能的。因此我会使用 RcArc 而不是引用。使用这些智能指针,您不需要注释生命周期和所有内容 "just works"。唯一的缺点:一点点运行时成本。

但不可能告诉您最佳解决方案:这实际上取决于手头的问题。

解释了为什么这不起作用,您应该考虑使用智能指针——Box 用于单一所有权,或 Rc/Arc 用于共享所有权。

就是说,您可以通过摆脱 Policeman 特性并使 set_next 成为 Officer:

的固有特性来做类似的事情(虽然不是很有用)
pub struct Officer<'a> {
    deduction: u8,
    next: Option<&'a Officer<'a>>,
}

impl<'a> Officer<'a> {
    pub fn new(deduction: u8) -> Officer<'a> {
        Officer {deduction, next: None}
    }
    fn set_next(&mut self, next: &'a Officer<'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

这有效(playground) because the struct Officer is covariant with respect to 'a. That means that if you have an Officer<'a>, you can treat it like an Officer<'b> as long as 'a: 'b; that is, when 'a outlives 'b, Officer<'a> is a subtype of Officer<'b>. This knowledge lets the compiler shrink the lifetimes of each reference in the way you probably expected at first. (There's another 关于您可能喜欢的变化,尽管它并不完全适用于您的情况。)

,所以 Policeman<'a> 不是 Policeman<'b> 的子类型。这剥夺了编译器调整生命周期的能力:引用 &'_ john 可能有更短的生命周期,但 Policeman<'_> 特征不能。这就是为什么即使是 Lukas 的 "quick fix" 也不适用于您的整个示例。

至少还有一种方法可以通过添加生命周期参数使原始示例工作,这样 set_next 就不会统一 &'?first Policeman<'?second> 中的两个生命周期,但从该更改中您只能得到一个额外的间接层——也就是说,它会使示例工作,但如果你添加 michael 谁报告给 martin,你会回到你开始的地方。