この記事は、Rust Advent Calendar その2 の25日目の記事です。
tokioいいらしいよ!という話を、最近良く聞くので、試してみようとしたんだけど、どうやって使ったら良いのかわからない・・・
サイトのサンプルとか見てみたけど、Futureとかよくわからない・・・
そんなお悩みのあなたに!!
私も最初そうでした。
で、少しずつ動かしながらやってみたら、だんだんわかってきたので、その軌跡をここに残しておきます。
対象読者
- Rustの基本的な構文がわかっている人。実際にコーディングしたことがある人。
- tokio による非同期処理の実装方法を知りたい人。
注意事項(2019.8.19追記)
本記事で使っている Future は、futures-rs( https://github.com/rust-lang-nursery/futures-rs )の0.1です。
今は Future もstdに取り込まれ、async/await も実装間近ですので、この記事の内容は参考程度に考えてもらうと良いかもしれません。
tokio
tokioのサイトはこれ。
ソース眺めたりするのに、手元に git clone しておくと良い。
https://github.com/tokio-rs/tokio
準備
プロジェクトを作ったら
Cargo.tomlを編集して、依存ライブラリに tokio と futures を追加
[dependencies]
tokio = "0.1.13"
futures = "0.1.25"
なにはともあれ tokio::run してみる
https://github.com/tokio-rs/tokio
ここにあるサンプルを見てみると、main()の最後に
// Start the Tokio runtime
tokio::run(server);
とある。
server は何者かわからないが、とにかくこれで tokioランタイムを起動する、ということらしい。
さて、tokio::run()の引数は何を要求されるのだろう?リファレンスを見てみる。
https://docs.rs/tokio/0.1/tokio/runtime/fn.run.html
pub fn run<F>(future: F)
where
F: Future<Item = (), Error = ()> + Send + 'static,
とある。
Future!!! つまり未来!!
未来って何?
またリファレンスを見てみる
https://docs.rs/futures/0.1.2/futures/trait.Future.html
pub trait Future {
type Item;
type Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error>;
fn wait(self) -> Result<Self::Item, Self::Error> where Self: Sized { ... }
fn boxed(self) -> BoxFuture<Self::Item, Self::Error> where Self: Sized + Send + 'static { ... }
//この下にずっと、たくさんメソッドが書いてある
Futureはトレイト。メソッドがいっぱいあって、なんじゃこりゃあ!ってなるけど、よく見ると、Required Method(=こちらで実装しないといけないメソッド)は、
fn poll(&mut self) -> Poll<Self::Item, Self::Error>
だけ。
あと、関連型として、ItemとErrorを定義せよ、とある。
んーじゃあ、試しに Future を実装する struct を作ってみますか。
その際考えないといけないのは、
- Itemに何を指定するか
- Errorに何を指定するか
- poll() の中で何をするか
- poll() は何を返せばいいか
Item, Error... 何を意味するのかは置いといて、もう一度 tokio::run() の定義を見てみると、
pub fn run<F>(future: F)
where
F: Future<Item = (), Error = ()> + Send + 'static,
Item = (), Error = () とある!じゃあどちらも () にしとけば良いってことね。
poll() の中で何をするか。とりあえず println! でなんか書いておこう。
poll() は何を返せばよいか。定義によると、
fn poll(&mut self) -> Poll<Self::Item, Self::Error>
poll は Pollを返せと。
・・・・解せぬ。
Pollの定義を見てみる。
type Poll<T, E> = Result<Async<T>, E>;
説明によると
Ok(Async::Ready(t)) ----- 成功!
Ok(Async::NotReady) ----- まだ結果が取れてない!
Err(e) ----- 失敗した!
このどれかを返せということらしい。
んじゃ、まずは成功を返しとけばいいか。
ということで、できたソース。
use tokio::prelude::*; //tokioでよく使うものをまとめて使えるようにする
struct FutureTest {
}
impl Future for FutureTest {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
println!("poll called!");
Ok(Async::Ready(()))
}
}
fn main() {
let f = FutureTest{};
tokio::run(f);
}
出力
poll呼んでくれた!
・・・で、これ何が嬉しいの?
その2につづく。
今回作ったソースはこちら
https://github.com/mas-yo/learn-tokio-step-by-step/tree/master/learn-tokio-1