使用Rust期货运行异步可变操作

Zól*_*ván 6 asynchronous future mutable rust rust-tokio

我正在使用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() {}
Run Code Online (Sandbox Code Playgroud)

作为一个简短的总结,在异步准备步骤之后,我正在尝试运行另一个写入字段的异步操作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
Run Code Online (Sandbox Code Playgroud)

我希望这应该有用,我不知道哪些可变参考仍然借用.我认为process()应该&mut self在将来返回后释放处理器,因此不应发生编译错误.

你能解释一下根本原因吗?如何将此示例更改为编译器接受?

She*_*ter 7

有时,如果将值拆分为多行,则更容易看到生命周期错误.让我们尝试使用有问题的闭包:

.and_then(move |_| {
    let c = processor_clone;
    let mut d = c.borrow_mut();
    let e = d.process(request);
    e
});
Run Code Online (Sandbox Code Playgroud)

如果你编译它...它的工作原理.如果我们尝试重新组合这些行,我们可能会失败:

.and_then(move |_| {
    let c = processor_clone;
    c.borrow_mut().process(request)
});
Run Code Online (Sandbox Code Playgroud)

这个工作:

.and_then(move |_| {
    let c = processor_clone;
    return c.borrow_mut().process(request);
});
Run Code Online (Sandbox Code Playgroud)

唯一的区别是显式返回和分号.这非常类似于当返回使用StdinLock的结果时,为什么借用stdin保留?所以让我们试试其中一个答案的建议,以启用非词汇生命.这允许您的原始代码也可以编译.

TL; DR:这是Rust目前的借用检查程序实现中的一个弱点,并且在某些时候会被神奇地修复.与此同时,我建议将其写成两行:

.and_then(move |_| {
    let mut c = processor_clone.borrow_mut();
    c.process(request)
});
Run Code Online (Sandbox Code Playgroud)