RUST言語 入門日記 (2)

RUST言語のAsync .await Future について本日の纏め。

RUSTはAsync .await Futureの定義やfuturesといった便利ツールを提供するが、asyncを実行するruntimeはtokioやasync-stdなど外部ライブラリになる。

Async .awaitについて

1. Async関数はFutureを返すだけであり、futures::executor::block_on やtokioと言ったexecutorがない限り実行されない。

1
2
3
4
5
6
async fn do_something() { 
println!("go go go !");
}
fn main() {
do_something();
}

2. .awaitは順番を保障するがスレッドをブロックしない。コンパイラがスマートにやってくれる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use futures::executor::block_on;

async fn hello_world() {
hello_cat().await;
println!("hello, world!");
}

async fn hello_cat() {
println!("hello, kitty!");
}
fn main() {
let future = hello_world();
block_on(future);
}

Futureについて

簡単なFuture

executorがpollを呼び出し、たくさんのFutureを上手く調達する。
pollは何回も呼ばれ、Pendingの場合次のwakeがセットされ、Readyの場合Futureが終了する。

1
2
3
4
5
6
7
8
9
trait SimpleFuture {
type Output;
fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
}

enum Poll<T> {
Ready(T),
Pending,
}

Pin Unpin !Unpinについて。

Pinはstruct。Unpinはtrait。!UnpinはUnpinをではないこと。

Rustのなか、あるインスタンスをmoveさせたくないときにPinを使う。
構造は単純で下記の通り。

1
2
3
pub struct Pin<P> {
pointer: P,
}

そのためPin<&mut T> , Pin<&T> , Pin<Box> どれでもいい。
ただTが!Unpinではないといけません。UnpinのものをPinしても意味がない。
例え、Pin<&mut u8>は&mut u8とほぼ同じでPinを使う意味がない。

RustではほどんどものがUnpinになる。しかしFutureは!UnpinであるためPinと組み合わせて書く場合がよくある。

普通のstructを!Unpinにしたい時はPhantomPinnedを使う。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
use std::pin::Pin;
use std::marker::PhantomPinned;

#[derive(Debug)]
struct Test {
a: String,
b: *const String,
_marker: PhantomPinned,
}

impl Test {
fn new(txt: &str) -> Pin<Box<Self>> {
let t = Test {
a: String::from(txt),
b: std::ptr::null(),
_marker: PhantomPinned,
};
let mut boxed = Box::pin(t);
let self_ptr: *const String = &boxed.as_ref().a;
unsafe { boxed.as_mut().get_unchecked_mut().b = self_ptr };

boxed
}

fn a(self: Pin<&Self>) -> &str {
&self.get_ref().a
}

fn b(self: Pin<&Self>) -> &String {
unsafe { &*(self.b) }
}
}

pub fn main() {
let test1 = Test::new("test1");
let test2 = Test::new("test2");

println!("a: {}, b: {}",test1.as_ref().a(), test1.as_ref().b());
// std::mem::swap(test1.get_mut(), test2.get_mut()); //pin されてるためswap出来ない。
println!("a: {}, b: {}",test2.as_ref().a(), test2.as_ref().b());
}