为了对Future调用poll,需要使用到Pin的特殊类型。本节就介绍一下Pin类型。
Async背后的一些原理
例子1
- 源码
<code>use
futures::executor;async
fn
async_function1
() {println!
("async function1 ++++ !"
); }async
fn
async_function2
() {println!
("async function2 ++++ !"
); }async
fn
async_main
() {let
f1 = async_function1();let
f2 = async_function2();let
f =async
move
{ f1.await
; f2.await
; }; f.await
; }fn
main
() { executor::block_on(async_main()); } /<code>
- 配置,在Cargo.toml中添加
<code>[dependencies]
futures
="0.3.4
/<code>
我们主要考虑async_main()函数中的async块(async函数也是一样,通过async都是转化为Future),实际背后会做如下工作: (1)为async块生成一个类似于如下的结构体:
<code>struct
AsyncFuture {
fut_one
:FutFunction1,
fut_two
:FutFunction2,
state
:State,
}
//state的定义可能如下
enum
State {
AwaitingFutFunction1,
AwaitingFutFunction2,
Done,
}
/<code>
(2)为其生成对应的poll函数:
<code>impl
Futurefor
AsyncFuture {type
Output
= ();fn
poll
(mut
self
: PinmutSelf
>, cx: &mut
Context<'_
>) -> Poll {loop
{match
self
.state { State::AwaitingFutFunction1 =>match
self
.fut_one.poll(..) { Poll::Ready(()) =>self
.state = State::AwaitingFutFunction2, Poll::Pending =>return
Poll::Pending, } State::AwaitingFutFunction2 =>match
self
.fut_two.poll(..) { Poll::Ready(()) =>self
.state = State::Done, Poll::Pending =>return
Poll::Pending, } State::Done =>return
Poll::Ready(()), } } } } /<code>
好,到这里,我们只是模拟编译器给async代码块进行了展开,那么和我们要讲的Pin有什么关系呢?
例子2
我们再考虑如下例子:
<code>async
fn
async_put_data_to_buf
(mut
buf: &[u8
]) { ... }async
fn
async_main
() {let
f =async
{let
mut
x = [0
;128
];let
read_into_buf_fut = async_put_data_to_buf(&mut
x); read_into_buf_fut.await
; }; f.await
; } /<code>
对于上面async_main中的async块,编译器为其生成的结构如下:
<code>struct
ReadIntoBuf
<'a
> { buf: &'a
mut
[u8
], }struct
AsyncFuture
{ x: [u8
;128
], read_into_buf_fut: ReadIntoBuf <'what_lifetime
?>, } /<code>
在AsyncFuture中,read_into_buf_fut.buf指向x,相当于是一个字引用(一个字段引用另一个字段)。但是如果AsyncFuture发生移动,x肯定也会发生移动,如果read_into_buf_fut.buf还是指向原来的值的话,则会变成无效。 而Pin就是为了解决此问题的。
Pin介绍
Pin类型包着指针类型,保证指针背后的值将不被移动。例如 Pin,Pin, Pin> 都保证 T 不会移动。
拿上面的例子来说,如果使用Pin<>就是将x对应的那块内存固定,这样即使AsyncFuture发生移动,但是x不会移动,那么read_into_buf_fut.buf不会变成悬垂引用。
大多数类型都没有移动的问题,这些类型实现了 Unpin trait。Unpin 类型指针可以自由从 Pin 中放入或取出。例如,u8 就是 Unpin的。
如果需要Future和Unpin一起使用,则需要使用Pin>或者pin_utils::pin_mut!,例子如下:
<code>use
pin_utils::pin_mut;fn
execute_unpin_future
(x:impl
Future + Unpin) { }let
fut =async
{ }; execute_unpin_future(fut);let
fut =async
{ };let
fut =Box
::pin(fut); execute_unpin_future(fut);let
fut =async
{ }; pin_mut!(fut); execute_unpin_future(fut); /<code>