Rust异步编程,Pin介绍

为了对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

Future

for

AsyncFuture {

type

Output

= ();

fn

poll

(

mut

self

: Pinmut

Self

>, 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>


分享到:


相關文章: