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()应该在返回未来后释放处理器&mut self,所以不应该发生编译错误。

你能解释一下根本原因吗?这个例子应该如何改变才能被编译器接受?

4

1 回答 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 的借用?,所以让我们尝试启用非词汇生命周期的答案之一的建议。这也允许您的原始代码编译。

TL;DR:这是 Rust 当前借用检查器实现的一个弱点,将在某个时候神奇地修复。同时,我建议把它写成两行:

.and_then(move |_| {
    let mut c = processor_clone.borrow_mut();
    c.process(request)
});
于 2018-05-17T14:35:25.903 回答