我正在尝试创建最简单的示例,使async fn hello()
最终打印出Hello World!
。这应该在没有任何外部依赖项(例如tokio
,仅是普通Rust和std
)的情况下发生。如果我们无需使用unsafe
就可以完成任务,则可加分。
#![feature(async_await)]
async fn hello() {
println!("Hello, World!");
}
fn main() {
let task = hello();
// Something beautiful happens here, and `Hello, World!` is printed on screen.
}
async/await
仍然是夜间活动,并且在可预见的将来可能会发生变化。Future
实现,我知道tokio
的存在。我模糊的理解是,首先,我需要Pin
完成任务。所以我继续
let pinned_task = Pin::new(&mut task);
但是
the trait `std::marker::Unpin` is not implemented for `std::future::GenFuture<[static generator@src/main.rs:7:18: 9:2 {}]>`
因此,我想当然会Box
,因此,我确定它不会在内存中移动。出乎意料的是,我遇到了同样的错误。
到目前为止我能得到的是
let pinned_task = unsafe {
Pin::new_unchecked(&mut task)
};
这显然不是我应该做的。即使这样,也可以说我已经获得Pin
的{{1}}的支持。现在,我需要以某种方式Future
。为此,我需要一个poll()
。
因此,我尝试四处寻找如何使用Waker
的方法。在doc上,获得Waker
的唯一方法似乎是与另一个接受Waker
的{{1}}一起。从那里我到达here,从那里here,我I缩在地板上开始哭泣。
答案 0 :(得分:2)
期货堆栈的这一部分并不是很多人想要实现的。我看到的粗略估计可能会有10个左右的实际实现。
也就是说,您可以通过遵循所需的功能签名来填写执行程序的基本方面,而这些方面非常受限制:
// Using Nightly from 2019-05-21
#![feature(async_await)]
async fn hello() {
println!("Hello, World!");
}
fn main() {
drive_to_completion(hello());
}
use std::{
future::Future,
ptr,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
fn drive_to_completion<F>(f: F) -> F::Output
where
F: Future,
{
let waker = my_waker();
let mut context = Context::from_waker(&waker);
let mut t = Box::pin(f);
let t = t.as_mut();
loop {
match t.poll(&mut context) {
Poll::Ready(v) => return v,
Poll::Pending => panic!("This executor does not support futures that are not ready"),
}
}
}
type WakerData = *const ();
unsafe fn clone(_: WakerData) -> RawWaker {
my_raw_waker()
}
unsafe fn wake(_: WakerData) {}
unsafe fn wake_by_ref(_: WakerData) {}
unsafe fn drop(_: WakerData) {}
static MY_VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
fn my_raw_waker() -> RawWaker {
RawWaker::new(ptr::null(), &MY_VTABLE)
}
fn my_waker() -> Waker {
unsafe { Waker::from_raw(my_raw_waker()) }
}
从Future::poll
开始,我们看到我们需要Pin
确定的未来和Context
。 Context
是由需要Waker
的RawWaker
创建的。 RawWaker
需要一个RawWakerVTable
。我们以最简单的方式创建所有这些片段:
由于我们不打算支持NotReady
案例,因此我们实际上不需要为该案例做任何事情,而可以惊慌失措。这也意味着wake
的实现可以是无操作的。
由于我们没有努力提高效率,所以我们不需要为唤醒者存储任何数据,因此clone
和drop
基本上也可以是无操作的。
确定未来的最简单方法是Box
,但这并不是最有效的方法。
如果您想支持NotReady
,最简单的扩展就是忙循环,永远轮询。一种更有效的解决方案是使用一个全局变量,该变量指示有人呼叫了wake
并阻止了该情况的发生。