Rustの並列処理を学ぼうぜ☆(^~^)?<その3>

読了目安:19分

前回の記事

Tokioクレートを学ぼうぜ☆?

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 あと2か月後の 2019年11月7日(金)に Rust 1.29 が出てきて Rustに Async/Await が言語実装されたら
これからTokioクレート を覚える意味がない上に 悪い癖が付くと思うが、
2年間隔で言語仕様を更新しますと言った Java 1.7 は6年待たされたからな☆ 延期はありうる☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 公式なんか信じてはいけないぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 Tokioクレートを覚えるのではなく、非同期処理を覚えなさい」

Crate tokio
Hello World!
Rust と非同期 IO の歴史
できる!futures入門

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 2006年に開発が始まったとされる Rust 言語の非同期処理は なぜ 2019年11月まで遅れているのかというと
2016年に AlphaZero が人間の棋譜を使わずに機械学習で イ・セドル に4勝1敗 するまで
ディープ・ラーニングに 使い道があると思われてなかったからだと思うが☆、」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ディープ・ラーニングの 精度 がビジネスに効く今、
精度を上げるため の 並列処理、マルチ・コア・プログラミング を今どき 書けなければ、
30年後の例えば 2050年に 使われているプログラミング言語 に入らないから
尻に火が付いて せっせと 慌てているのだろう☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 お父んは なんで Rust なんか使ってるんだぜ☆? 安牌を捨てるなら PythonGo なのに……☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 平岡さんが Rust を使うとか耳にして 流行り に飛びついただけなのよ!
飛びついたはいいものの、 流行ってなかった のよ!」

KIFUWARABE_80x100x8_01_Futu.gif
「 そういえば お父んは Java 1.1 とか、 Paradox とか触ってたらしいな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 先輩は Delphi を使っていた☆ もう耳にもしない☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Java Script とか、 C# とか、 C++ とか、 お前らがよく耳にするプログラム言語は
Webのクライアント・サイド書くなら Java Script とか、
Unityでマルチ・プラット・フォーム対応のゲーム書くなら C# とか、
これをするなら この言語、と言ったような 利用シーンを持っていて シェアを獲っている☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 だいたい プログラム言語が生き残るルートは 2つあるようだぜ☆
大きいターゲットのシェアを獲るか、くそレガシーとして生き残る かの2つだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 レガシーは生き残らないのよ、まだ死に損なっているだけなの」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Tokioクレートも 十分レガシーだろう……☆
なんか レガシーばっか やることになるな……☆」

非同期の例外処理

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 非同期処理を覚えていこう☆ よく目にするのは 非同期の例外処理 だぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 非同期の例外処理って何だぜ☆? 同期の例外処理と何が違うんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 まず 例外処理とは何か、を説明する☆」

20190928comp42a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 りんご が袋に入っているとしよう☆
奴隷には りんごを3個~5個 袋に詰めなさい、と指示しておいた☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 奴隷は労働力として大切にされていたらしいな☆ 使いつぶしても代わりの利く日本人とは違って☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 労働力じゃなければ大切にされなかったんだけどね」

20190928comp42a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 3個~5個じゃない袋も 混ざっているだろう☆
もしかすると 梨 が混ざってるかもしれないしな☆ そういうものは エラー として個別の対応をする☆
りんごを袋から取り出して 詰める工程に差し戻して やりなおしたり、
めんどくさければ 捨ててもいい☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 それとは別に☆」

20190928comp42a3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 りんご は いっぱいあるのに、袋が 0枚 だったら 奴隷たちはどうするだろうか☆?」

KIFUWARABE_80x100x8_01_Futu.gif
「 仕事を休んでビールでも飲むぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 永遠にエラーにするのも 無駄よねぇ」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そういうときは 例外処理 として 主に報告を上げ、
安全な状態にして 作業をストップすることを目指す☆
あるいは リトライ を目指す場合もあるが、上図の状況では リトライしても無駄なんで 終われだぜ☆」

Enum std::result::Result

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑Rust言語では エラー処理 の書き方の模範があるので まず それを説明しよう☆」

20190928comp43a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 std::result::Result という列挙型がある☆
処理がグッドな終わり方をしたら Ok と一緒に値を返し、
処理がバッドな終わり方をしたら Err と一緒に値を返せだぜ☆」

let server = listener.incoming().for_each(|socket| {
        Ok(())
    })
    .map_err(|err| {
        println!("Error = {:?}", err);
    });

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑例えば こういうコードを目にすることがあるが……☆」

let server = listener.incoming().for_each( ☆ ).map_err( ☆ );

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 コールバック関数を省略すると ↑こういう見た目をしている☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 C言語とかC#言語からRust言語に来ると、 メソッド・チェーン は見慣れないのよね~」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 もう少し細かく見ていくと……☆」

20190928comp43a2b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 サーバーというのは、 クライアントを 3つも 4つも、 多くを相手にしないといけない☆」

let server = listener.incoming().for_each( ☆ ).map_err( ☆ );
                     ^^^^^^^^^^^

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ということは、この .incoming() という関数は、タイミングが重なれば
一度に 複数のクライアントを返すこともあるわけだぜ☆」

let server = listener.incoming().for_each( ☆ ).map_err( ☆ );
                                ^^^^^^^^^

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 だから後ろに、 for_each() をチェーンしているな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 .for_each(☆) の星の中に クライアント1つに対応したクロージャを書けばいいわけかだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 理解が早いな……☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 接続してきたのに失敗するクライアントとかいたら どう対応すんの?」

let server = listener.incoming().for_each( ☆ ).map_err( ☆ );
                                               ^^^^^^^^

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 メソッド・チェーンのうしろの .map_err() でログでも出せだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 なんで .for_each() のうしろに .map_err() が くっついてんの?」

20190928comp44a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 例えば さっきの りんご の例だと Result型を使って ↑上図のように Ok と Err に分けて
数を付けることができるわけだが……☆」

20190928comp44a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Ok と Err に分かれていると考えろだぜ☆ すっきりしてるだろ☆
このような見方を 写像になっている と呼んだりする☆ 写像を英語で言うと map☆」

let server = listener.incoming().for_each(|socket| {
        Ok(())
    })
    .map_err(|err| {
        println!("Error = {:?}", err);
    });

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これで ↑このコード の見方も見えてきただろ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 Ok(()) って何なの? Ok は震えてるの?」

20190928comp44a3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Ok だが、返す中身はないということだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 じゃあ そこで Err(5) とか書いたら、制御はどこに返るの?
うしろの .map_err() ?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 どうなるんだろな☆?
fall through(フォール スルー)するんだろうか☆? 分かんないぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 しかし お父ん☆?」

let server = listener.incoming().for_each( ☆ ).map_err( ☆ );
^^^^^^^^^^^^

KIFUWARABE_80x100x8_01_Futu.gif
「 なんで 返ってくるのは server なんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ぱっと見た感じ、それは Future (フューチャー; 先物) なんだろうと想像が働くよな☆
フューチャーの説明は前の記事で、した☆」

tokio::run(server);

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 あとの方で .run(server) してれば、 Future だと分かるぜ☆
同期処理と、非同期処理を、違いを気にせず書けるようにしてるから run という名前なんだろうが、
プロミス型の非同期処理では await と書く場合もある☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 この調子で むずかしいコードに目を慣らしていこうぜ☆」

handle client

Example: An Echo Server

let server = listener.incoming().for_each( ☆ ).map_err( ☆ );
                                          ^^^^

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クライアント1つごとに やる処理を調べていこうぜ☆」

let server = listener.incoming().for_each(|socket| {
  // split the socket stream into readable and writable parts
  let (reader, writer) = socket.split();
  // copy bytes from the reader into the writer
  let amount = io::copy(reader, writer);

  let msg = amount.then(|result| {
    match result {
      Ok((amount, _, _)) => println!("wrote {} bytes", amount),
      Err(e)             => println!("error: {}", e),
    }

    Ok(())
  });

  // spawn the task that handles the client connection socket on to the
  // tokio runtime. This means each client connection will be handled
  // concurrently
  tokio::spawn(msg);
  Ok(())
})

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クライアントから送られてきた文字列を そのまま返す、エコー・サーバー(Echo server)というサンプル・プログラムだぜ☆
解説していこう☆」

let server = listener.incoming().for_each(☆)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クロージャーを省くとこの形だぜ☆ この形はもう見たな☆」

|socket| {
  // split the socket stream into readable and writable parts
  let (reader, writer) = socket.split();
  // copy bytes from the reader into the writer
  let amount = io::copy(reader, writer);

  let msg = amount.then(|result| {
    match result {
      Ok((amount, _, _)) => println!("wrote {} bytes", amount),
      Err(e)             => println!("error: {}", e),
    }

    Ok(())
  });

  // spawn the task that handles the client connection socket on to the
  // tokio runtime. This means each client connection will be handled
  // concurrently
  tokio::spawn(msg);
  Ok(())
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クロージャーはこうだぜ☆ この中身を解説していこう☆」

|socket| {☆}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クロージャーは こういう形をしている☆
引数は socket だぜ☆」

  // split the socket stream into readable and writable parts
  let (reader, writer) = socket.split();
  // copy bytes from the reader into the writer
  let amount = io::copy(reader, writer);

  let msg = amount.then(|result| {
    match result {
      Ok((amount, _, _)) => println!("wrote {} bytes", amount),
      Err(e)             => println!("error: {}", e),
    }

    Ok(())
  });

  // spawn the task that handles the client connection socket on to the
  // tokio runtime. This means each client connection will be handled
  // concurrently
  tokio::spawn(msg);
  Ok(())

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クロージャーの本体は ↑こうだな☆」

  let (reader, writer) = socket.split();

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ソケットは、読取ストリームと 書込ストリームの2つに分けるのが定番なようだぜ☆ 真似しよう☆」

  let amount = io::copy(reader, writer);

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 io::copy() というのは 読取ストリームから入ってきたものを、書込ストリームにそのまま 送り出すようだぜ☆
これが エコー の仕組みだな☆
で、返り値が amount だが、これは 非同期処理の Future と見るべきだろう☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 Function の返り値なのか、 非同期処理の Future なのか、
どっちが返ってきているのか どうやって見分けるんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 よい見分け方はない☆ 見分けにくいよな☆
これが改善されるのか、そのまま 言語実装になるのかは 今の時点では分からないぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 プログラム言語は 型推論 が自動で行われるのが流行りですから、
Functionの返り値なのか、Futureなのかは 見分けられなくなるように進化 していく流れなのよ。
人類はボトルネックなの。 コンピューターが見分けてくれるから、人類は下手に見分けようとしなくていいのよ」

KIFUWARABE_80x100x8_01_Futu.gif
「 Functionの返り値と、Future の使い勝手が 合同 なら 見分けられなくなるように進化 するのはいいが、
使い勝手は異なるから 結局 見分けないと使えないんだろ☆? ややこしいぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 C言語から Rust言語に来た人なら、何段階か頭をギアチェンジしないといけないよな☆
そのアップデートができないと ここで終わりだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 プログラムの文法は こういうものだ、と 頭の中で形ができあがっちゃうと 定型文を組み合わせるだけの文学になってしまうのよ。
文法を発明する 数学 の頭をしていれば コロっと より新しいものを取り込める普遍的な記法 の方へ乗り換えられるのよ」

KIFUWARABE_80x100x8_01_Futu.gif
「 そんな説明では 分からないんで☆」

let msg = amount.then(|result| {
    match result {
      Ok((amount, _, _)) => println!("wrote {} bytes", amount),
      Err(e)             => println!("error: {}", e),
    }

    Ok(())
  });

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 続きのコードを見てみよう☆」

let msg = amount.then(☆);

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クロージャーを省くと ↑こんな形をしている☆
amount は Future だとしよう☆ .then() というメソッドは、何かあったときに
その引数に渡されたクロージャーを実行するのだろう☆
Tokioクレートの話題をしていて、クロージャーを使ってるなら 非同期処理っぽいよな☆」

|result| {
    match result {
      Ok((amount, _, _)) => println!("wrote {} bytes", amount),
      Err(e)             => println!("error: {}", e),
    }

    Ok(())
  }

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クロージャーだけ見ると ↑こんな形をしている☆
Result型の result 引数が渡ってくるんで、目新しいのは match構文で スイッチしてるところだけだな☆」

    match result {
      Ok((amount, _, _)) => println!("wrote {} bytes", amount),
      Err(e)             => println!("error: {}", e),
    }

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Rust言語では switch文が無いので その代わりに match 構文を使う☆
Ok の方は値が タプルになっていて (amount, _, _) と書いてある☆ まあ、Result型の値は様々だからな☆
見たところ 何かの量を表す数 が入ってそうだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 なにをやりたがっているのか 想像しながら プログラムを読まなくちゃいけないのね。
大昔のアセンブリ言語に先祖返りしたみたい」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 で、エラーもメッセージを表示しているだけなんで……☆」

    Ok(())

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 このクロージャーは 必ず 値なしのOk で終わるな☆」

let msg = amount.then(☆);
^^^^^^^^^

KIFUWARABE_80x100x8_01_Futu.gif
「 Ok は .then( ) メソッドの中のどこかへ返してるんだろ☆
そして .then( ) メソッドは msg を返している☆ これも Future かだぜ☆?」

  tokio::spawn(msg);
  Ok(())

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 msg は ::spawn( ) メソッドに渡してるな☆ スレッドを作るメソッドなわけだが、
msg は コールバック関数なんだろうか☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 tokio::run( ) じゃなくて tokio::spawn( ) を使うの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 run( ) は 開始の合図みたいなもんで、
それ以外は 開始したあとに どう動くかといった パイプラインの設計図を作っているのだろう☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 同期のプログラムを組んでいるのか、非同期処理のパイプラインの設計図を作っているのか、見た目から分からん……☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 同期処理と、非同期処理を 同じ見た目で書ける というのも ウリ なんだろうけどな☆」

Future を自作しようぜ☆(^~^)

Implementing futures

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑フーチャーを使うだけでなく、自分で書く方法も説明されている☆」

trait Future {
    type Item;
    type Error;
    fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error>;
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑覚えることは3つしかないぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 Item とかいう どうとでも受け取れる名前を付けられると 何なのか わかんないよな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 しかも type だし」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 その Item も、 Error も、 poll メソッドで使う型なわけなんで、
よく見てしまえば、結局 poll (ポール)メソッド1つ 定義しているだけだぜ☆」

    fn poll(&mut self) -> ☆;

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 poll メソッドの引数は 無いようなもんなんで……☆」

    ☆ -> Result<Async<Self::Item>, Self::Error>;

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 じゃあ poll メソッドで一番むずかしいのは 返り値 ということになるが……☆」

20190928comp43a1.png

    Result<☆, ☆>

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Result 型は前に説明したな☆」

    Async<Self::Item>,
    Self::Error

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 すると Ok のとき Async<Self::Item> 型を一緒に返すし、
Err のとき Self::Error 型を一緒に返すということだな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Self::Item 型とか、 Self::Error 型とか聞いたことないぞ、何だ、というと……☆」

trait Future {
    type Item;
    type Error;
    fn ☆;
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Future トレイトで Item と、 Error の型は指定できるな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 じゃあ Future って、その成分の100%は poll メソッドの返り値 ってことなの?
じゃあ Future は、返り値なの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 まあ、まさに そういうことなのだろう☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 Async< > なんていう型 聞いたことないんだが☆」

Enum futures::Async

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑聞いたことなければ、自分で調べろだぜ☆」

20190928comp43a1b2.png

pub enum Async<T> {
    Ready(T),
    NotReady,
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ Ready 準備ができたか、 NotReady できてないかの2択だな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 Ready の対義語が NotReady ってのも なんか ダサいわねぇ」

KIFUWARABE_80x100x8_01_Futu.gif
「 じゃあ poll メソッドは、準備できたItem、準備できてない、エラーError の3パターンの終わり方があるぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そういうことだな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 Future って、それだけなの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 それだけだな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 実装サンプルを見てみようぜ☆?」

Implementing futures

extern crate futures;

// `Poll` is a type alias for `Result<Async<T>, E>`
use futures::{Future, Async, Poll};

struct HelloWorld;

impl Future for HelloWorld {
    type Item = String;
    type Error = ();

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(Async::Ready("hello world".to_string()))
    }
}

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 糖衣構文みたいな技を使ってるわよ。 Result<Async<☆>,☆> なんて書かずに Poll<☆,☆> と書けばいいみたいよ」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 それぐらい簡単にしてくれなければ 書きたくないよな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 なんか 簡単な話しなんだな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 このコードは 常に Ready 準備できていて、 hello world を返すぜ☆
じゃあ どうやって この非同期処理を実行するかだな☆」

extern crate futures;

use futures::{Future, Async, Poll};
use std::fmt;

struct Display<T>(T);

impl<T> Future for Display<T>
where
    T: Future,
    T::Item: fmt::Display,
{
    type Item = ();
    type Error = T::Error;

    fn poll(&mut self) -> Poll<(), T::Error> {
        let value = match self.0.poll() {
            Ok(Async::Ready(value)) => value,
            Ok(Async::NotReady) => return Ok(Async::NotReady),
            Err(err) => return Err(err),
        };

        println!("{}", value);
        Ok(Async::Ready(()))
    }
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Display 構造体が poll メソッドを持っているような例が サンプルで示されているぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 詳しく☆」

extern crate futures;

use futures::{Future, Async, Poll};
use std::fmt;

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 std::fmt::Display トレイトを利用するわけだな☆」

Trait std::fmt::Display

pub trait Display {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error>;
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 この fmt メソッドは例えば println!("{}", value); みたいなコードがあるとき、
"{}" みたいな文字列をフォーマットと呼ぶが、この波括弧を 別の文字列に置換してくれる働きをするぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 この Display トレイトを、非同期処理で利用できるようにしようというサンプルだぜ☆」

struct Display<T>(T);

impl<T> Future for Display<T>
where
    T: Future,
    T::Item: fmt::Display,
{
    ☆
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 分かりづらいが、あとで Display( ☆ ) みたいなコンストラクターみたいな書き方ができる構造体を作っている☆
その Display構造体に対して、 T が Future で、 FutureのItemが std::fmt::Display のとき、トレイトを実装する……、
みたいなことが書いてある☆ さらに本体を見てみよう☆」

    type Item = ();
    type Error = T::Error;

    fn poll(&mut self) -> Poll<(), T::Error> {
        ☆
    }

KIFUWARABE_80x100x8_01_Futu.gif
「 これは Feature トレイトの実装だな☆ 準備ができているときは () 値はなしで、
エラーのときは多分 FutureのErrorか☆」

        let value = match self.0.poll() {
            Ok(Async::Ready(value)) => value,
            Ok(Async::NotReady) => return Ok(Async::NotReady),
            Err(err) => return Err(err),
        };

        println!("{}", value);
        Ok(Async::Ready(()))

KIFUWARABE_80x100x8_01_Futu.gif
「 self.0 という書き方が嫌らしいが トレイトを実装したオブジェクトの最初のプロパティ という意味かだぜ☆?
その最初のプロパティも .poll() メソッドを持っている……☆?
match 構文で3行並んでいるのは 準備できている、準備できていない、エラー の3つだな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 HelloWorld トレイトは必ず 準備できているんで、その他の分岐は適当に作ってるんだろう☆」

extern crate tokio;

let future = Display(HelloWorld);
tokio::run(future);

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 あとは tokioクレートで 非同期処理を run するだけだな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 必ず準備できているような非同期処理なら、糖衣構文があるみたいよ?」

#[macro_use]
extern crate futures;

use futures::{Future, Async, Poll};
use std::fmt;

struct Display<T>(T);

impl<T> Future for Display<T>
where
    T: Future,
    T::Item: fmt::Display,
{
    type Item = ();
    type Error = T::Error;

    fn poll(&mut self) -> Poll<(), T::Error> {
        let value = try_ready!(self.0.poll());
        println!("{}", value);
        Ok(Async::Ready(()))
    }
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 try_ready!( ) というマクロがあるのかだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 結構長くなったんで、続きは次の記事でだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 まだだぜ☆ 完全なるソースを示せだぜ☆」

Implementing futures - example source

Cargo.toml

[dependencies]
tokio = "0.1"
futures = "0.1.26"

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 クレートは自分で crates.io から検索しような☆」

main.rs

//!
//! cargo new ep1
//! cd C:\Users\むずでょ\OneDrive\ドキュメント\practice-rust\tokio\impl-futu
//! cargo check
//! cargo build
//! cargo run
//! 
//! [Implementing futures](https://tokio.rs/docs/futures/basic/)
//!

extern crate tokio;
extern crate futures;

use futures::{Future, Async, Poll, try_ready};
use std::fmt;


struct HelloWorld;

impl Future for HelloWorld {
    type Item = String;
    type Error = ();

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(Async::Ready("hello world".to_string()))
    }
}


struct Display<T>(T);

impl<T> Future for Display<T>
where
    T: Future,
    T::Item: fmt::Display,
{
    type Item = ();
    type Error = T::Error;

    fn poll(&mut self) -> Poll<(), T::Error> {
        let value = try_ready!(self.0.poll());
        println!("{}", value);
        Ok(Async::Ready(()))
    }
}

fn main() {
    let future = Display(HelloWorld);
    tokio::run(future);
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ソースはこんな感じだぜ☆」

次回に続く

ツイッターでシェア
みんなに共有、忘れないようにメモ

むずでょ@きふわらべ第29回世界コンピューター将棋選手権一次予選36位

光速のアカウント凍結されちゃったんで……。ゲームプログラムを独習中なんだぜ☆電王戦IIに出た棋士もコンピューターもみんな好きだぜ☆▲(パソコン将棋)WCSC29一次予選36位、SDT5予選42位▲(パソコン囲碁)AI竜星戦予選16位

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント