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

标签 design-patterns rust chain-of-responsibility

我正在尝试在 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
  • johnvincent ( vincent 范围内)
  • martinjohn (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 selfjohnself这里。所以'a活不过 john .换句话说:'j活下去 'a , 表示 'j: 'a .
  • 我们有 next: &'a ...nextvincent , 所以(就像上面一样), 'a活不过 vincent . 'v活下去 'a => 'v: 'a`。
  • 最后,'aPoliceman<'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 !所以全类型vincentOfficer<'j> .这反过来告诉编译器 vincent借用一生的东西j .但是vincent生命周期长于 'j ,所以它比它的借用还长!那很糟。这就是编译器提示的原因。

    整个事情真的很复杂,我想大多数人在阅读了我的解释后,和我阅读了大多数数学证明后的感觉完全一样:每一步都有意义,但结果并不直观。也许这会稍微改善情况:

    set_next()函数要求所有生命周期为 'a ,我们对程序中的所有生命周期都施加了很多限制。这很快导致限制的矛盾,就像这里发生的那样。

    我的小例子的快速修复

    ...是删除'a来自 self范围:
    fn set_next(&mut self, next: &'a Policeman<'a>)
    

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

    更通用的解决方案

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

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

    关于design-patterns - 如何使用特征对象链实现责任链模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46743353/

    相关文章:

    design-patterns - "Chain of responsibility"和 "Strategy"模式有什么区别?

    c++ - 哪种设计模式最合适?

    design-patterns - 没有 dbcontext 时的工作单元模式

    java - 是否建议在静态内部类中使用静态 block

    rust - 如何在 Rust 中释放通过 FFI 分配的 *char?

    c# - 修改后的责任链

    Swift-如何使用函数或其他模式初始化结构实例

    string - 为什么 String::from(&str) 和 &str.to_string() 在 Rust 中表现不同?

    http - Hyper 找不到服务器模块

    java - 让一个请求由责任链模式中的多个处理程序处理