使用Rust futures运行异步可变操作

7

我正在使用tokio-rs构建一个Rust服务,并且到目前为止,我对这个技术栈感到满意。现在,我正在尝试链接包含写操作的异步操作,并且在借用检查器方面遇到了困难。

我的简化的最小代码示例如下:

extern crate futures; // 0.1.21

use futures::Future;
use std::{cell::RefCell, rc::Rc};

trait RequestProcessor {
    fn prepare(&self) -> Box<Future<Item = (), Error = ()>>;
    fn process(&mut self, request: String) -> Box<Future<Item = (), Error = ()>>;
}

struct Service {
    processor: Rc<RefCell<RequestProcessor>>,
}

impl Service {
    fn serve(&mut self, request: String) -> Box<Future<Item = (), Error = ()>> {
        let processor_clone = self.processor.clone();
        let result_fut = self
            .processor
            .borrow()
            .prepare()
            .and_then(move |_| processor_clone.borrow_mut().process(request));
        Box::new(result_fut)
    }
}

fn main() {}

作为简短的总结,在异步准备步骤之后,我正在尝试运行另一个异步操作,该操作写入self的一个字段。如果没有可变性,使用普通的Rc成员可以轻松实现,但是可变性会导致出现以下错误:
error[E0597]: `processor_clone` does not live long enough
  --> src/main.rs:22:32
   |
22 |             .and_then(move |_| processor_clone.borrow_mut().process(request));
   |                                ^^^^^^^^^^^^^^^                             - `processor_clone` dropped here while still borrowed
   |                                |
   |                                borrowed value does not live long enough
   |
   = note: values in a scope are dropped in the opposite order they are created

我觉得这应该能够正常工作,我没有看到可变引用仍然被借用的地方。 我认为process()应该在返回future后释放处理器的&mut self,因此不应发生编译错误。
您能否解释一下其中的原因? 如何更改此示例才能被编译器接受?

2
感谢您提供高质量的最小示例。 - Shepmaster
1个回答

8
有时候,如果将值分成多行,就更容易看到生命周期错误。让我们尝试一下在问题中使用闭包的方法:
.and_then(move |_| {
    let c = processor_clone;
    let mut d = c.borrow_mut();
    let e = d.process(request);
    e
});

如果您编译这个代码,它可以正常工作。如果我们尝试重新组合这些行,就会导致失败:
.and_then(move |_| {
    let c = processor_clone;
    c.borrow_mut().process(request)
});

而这需要工作:
.and_then(move |_| {
    let c = processor_clone;
    return c.borrow_mut().process(request);
});

唯一的区别在于显式返回和分号。这与当返回消耗StdinLock结果时,为什么stdin借用要保留?非常相似,因此让我们尝试一个答案的建议,以启用非词法生命期。这也可以使您的原始代码编译通过。

简而言之:这是Rust当前借用检查器实现中的一个缺陷,将在某些时刻神奇地修复。同时,我建议将其写成两行:

.and_then(move |_| {
    let mut c = processor_clone.borrow_mut();
    c.process(request)
});

1
糟糕,这是一个非常烂的泄漏抽象。感谢您为我们澄清了这个问题。 - wigy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接