运行 带有 Rust 期货的异步可变操作
Running asynchronous mutable operations with Rust futures
我正在使用 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
,所以应该不会出现编译错误。
能否请您解释一下根本原因?这个例子应该如何修改才能被编译器接受?
有时,如果将值拆分为多行,则更容易看到生命周期错误。让我们尝试一下有问题的闭包:
.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);
});
唯一的区别是明确的 return 和分号。这与 When returning the outcome of consuming a StdinLock, why was the borrow to stdin retained?, so let's try the suggestion of one of the answers to enable non-lexical lifetimes 非常相似。这也允许您的原始代码进行编译。
TL;DR:这是 Rust 当前借用检查器实现中的一个弱点,将在某个时候神奇地修复。同时,我建议将其写成两行:
.and_then(move |_| {
let mut c = processor_clone.borrow_mut();
c.process(request)
});
我正在使用 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
,所以应该不会出现编译错误。
能否请您解释一下根本原因?这个例子应该如何修改才能被编译器接受?
有时,如果将值拆分为多行,则更容易看到生命周期错误。让我们尝试一下有问题的闭包:
.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);
});
唯一的区别是明确的 return 和分号。这与 When returning the outcome of consuming a StdinLock, why was the borrow to stdin retained?, so let's try the suggestion of one of the answers to enable non-lexical lifetimes 非常相似。这也允许您的原始代码进行编译。
TL;DR:这是 Rust 当前借用检查器实现中的一个弱点,将在某个时候神奇地修复。同时,我建议将其写成两行:
.and_then(move |_| {
let mut c = processor_clone.borrow_mut();
c.process(request)
});