如何在不使用任何外部依赖项的情况下执行 async/await 函数?
How do I execute an async/await function without using any external dependencies?
我正在尝试创建最简单的示例,可以让 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
的存在。
- 我只是想自学标准库 futures 的内部工作原理。
我无奈笨拙的努力
我模糊的理解是,首先,我需要 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
ned Future
。现在我需要以某种方式 poll()
它。为此,我需要一个 Waker
.
所以我试着四处寻找如何获得 Waker
。在 doc it kinda looks like the only way to get a Waker
is with another new_unchecked
that accepts a RawWaker
. From there I got here and from there here 上,我蜷缩在地板上开始哭泣。
期货堆栈的这一部分并不打算由许多人实施。我看到的粗略估计,可能会有10个左右的实际实现。
也就是说,您可以通过遵循所需的函数签名来填写极其有限的执行程序的基本方面:
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
, we see we need a Pin
ned future and a Context
. Context
is created from a Waker
which needs a RawWaker
. A RawWaker
needs a RawWakerVTable
开始。我们以最简单的方式创建所有这些作品:
由于我们不尝试支持 NotReady
个案例,因此我们实际上不需要为该案例做任何事情,而是可以恐慌。这也意味着 wake
的实现可以是 no-ops。
因为我们不是为了提高效率,所以我们不需要为我们的waker存储任何数据,所以clone
和drop
基本上可以是no-ops也是。
确定未来最简单的方法是Box
它,但这不是最有效的方法。
如果你想支持NotReady
,最简单的扩展就是有一个繁忙的循环,永远轮询。一个稍微更有效的解决方案是使用一个全局变量来指示有人调用 wake
并阻止它变为真。
我正在尝试创建最简单的示例,可以让 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
的存在。 - 我只是想自学标准库 futures 的内部工作原理。
我无奈笨拙的努力
我模糊的理解是,首先,我需要 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
ned Future
。现在我需要以某种方式 poll()
它。为此,我需要一个 Waker
.
所以我试着四处寻找如何获得 Waker
。在 doc it kinda looks like the only way to get a Waker
is with another new_unchecked
that accepts a RawWaker
. From there I got here and from there here 上,我蜷缩在地板上开始哭泣。
期货堆栈的这一部分并不打算由许多人实施。我看到的粗略估计,可能会有10个左右的实际实现。
也就是说,您可以通过遵循所需的函数签名来填写极其有限的执行程序的基本方面:
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
, we see we need a Pin
ned future and a Context
. Context
is created from a Waker
which needs a RawWaker
. A RawWaker
needs a RawWakerVTable
开始。我们以最简单的方式创建所有这些作品:
由于我们不尝试支持
NotReady
个案例,因此我们实际上不需要为该案例做任何事情,而是可以恐慌。这也意味着wake
的实现可以是 no-ops。因为我们不是为了提高效率,所以我们不需要为我们的waker存储任何数据,所以
clone
和drop
基本上可以是no-ops也是。确定未来最简单的方法是
Box
它,但这不是最有效的方法。
如果你想支持NotReady
,最简单的扩展就是有一个繁忙的循环,永远轮询。一个稍微更有效的解决方案是使用一个全局变量来指示有人调用 wake
并阻止它变为真。