我在尝试实现Future
时遇到了一个障碍。
我需要在我的poll
函数中poll
另一个async
函数,它只接受&str
。它看起来像这样:
async fn make_query(query: &str) -> i32 {
// Magic happens
5
}
字符串
由于这是一个来自另一个库的函数,我不能简单地改变它的签名来获得值的所有权。
现在我的结构Executor
,我想实现Future
,它包含一个String
,当调用make_query
时应该引用它:
struct Executor {
query: String,
future: Mutex<Option<Pin<Box<dyn Future<Output = i32>>>>>
}
impl Future for Executor {
type Output = i32;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut lock = self.future.lock().unwrap();
if let None = *lock {
// This is where the error occurs
*lock = Some(Box::pin(make_query(&self.query)))
}
let future = lock.as_mut().unwrap();
pin!(future).poll(cx)
}
}
型
现在,这样做的目的是我可以.await
my Executor
自动进行查询:
#[tokio::main]
async fn main() {
let result = Executor {
query: "foo".to_string(),
future: Mutex::new(None)
}.await;
println!("Result is {result}");
}
型
现在,显然Mutex
要求传递给它的每个引用都是'static
?我收到了这些错误消息:
error: lifetime may not live long enough
--> src/main.rs:23:26
|
16 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
| - let's call the lifetime of this reference `'1`
...
23 | *lock = Some(Box::pin(make_query(&self.query)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`
error[E0597]: `self` does not live long enough
--> src/main.rs:23:47
|
16 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
| ---- binding `self` declared here
...
23 | *lock = Some(Box::pin(make_query(&self.query)))
| ---------------------^^^^--------
| | |
| | borrowed value does not live long enough
| cast requires that `self` is borrowed for `'static`
...
29 | }
| - `self` dropped here while still borrowed
型
如何告诉Rust Executor
只有在被完全轮询时才会被丢弃?我不能改变poll
的函数签名,因为这会违反trait规范。
或者,更有可能的是,做我想做的事情的正确方法是什么?
这里有一个playground的链接。
PS:我知道调用.await
来执行查询可能会被认为是一种“反模式”。
溶液
查看drewtato's answer以获得解释。
解决方案(对我来说)是从我的结构体中删除future
字段:
struct Executor {
query: String
}
型
然后,不实现Future
,而是实现IntoFuture
,当使用.await
时,它会被隐式调用。在这里,我们可以使用async move {}
来移动值,而不仅仅是引用它。
注意,此时type IntoFuture = impl Future
是不稳定的,因此我不得不使用Pin<Box<dyn Future>>
impl IntoFuture for Executor {
type Output = i32;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
make_query(&self.query).await
})
}
}
型
现在你可以在Executor
上调用.await
:
#[tokio::main]
async fn main() {
let res = Executor {
query: "foo".into()
}.await;
println!("Result is {res}"); // 5
}
型
2条答案
按热度按时间kuuvgm7e1#
你想做一个self-referential struct, which is not safely possible。
看起来你试图让这个异步函数
'static
,如果你试图派生它,这是有用的。你可以通过创建一个async block来实现这一点。字符串
这个异步块实际上是自引用的,但是异步块是由rust专门处理的。如果你想手工创建它,你将需要不安全的代码。
注意,calling 一个async函数不起作用,只有 * waiting * 起作用,所以延迟
make_query
调用没有任何意义。如果你想在make_query
之前做一些事情,你可以简单地把它放在async块中。我不明白你为什么要用互斥锁。如果你需要这个,把它加到问题里。完全切除可能没问题。
每个trait对象都包含一个生存期,所以当你声明一个trait对象而没有像
dyn Future<Output = i32>
这样的生存期时,它通常会变成dyn Future<Output = i32> + 'static
。如果你想在trait对象中存储非'static
数据,你需要将它添加到你的类型中。型
然而,只有当你的结构不是自引用的时候,这才是正确的。
iugsix8n2#
处理的方法是创建一个 Package 器函数,该函数接受owned
String
,完成工作,并返回String
,以便重用它。这样,你得到了一个不包含生存期的future(Rust编译器为你处理自引用),你可以自由地将它存储在结构体中:我还将向您介绍
tokio_util::sync::ReusableBoxFuture
,它允许您在这种情况下通过只为将来分配一次空间来保存分配。在这种情况下,您不需要它,因为您只使用了一次未来,但在类似的情况下,它可能很有用。