Rustのtokioをコードリーディングする
私は入門書を読み終えたレベルでサンプルプログラム以上のものは作ったことがない程度です
まだtokioと聞いてアイドルグループが頭に浮かぶレベルの者ですが、いろいろ学んでみたいと思います
↓以下、リポジトリなど
[repository] https://github.com/tokio-rs/tokio
[contributing] https://github.com/tokio-rs/tokio/blob/master/CONTRIBUTING.md
[readme] https://github.com/tokio-rs/tokio/blob/master/README.md
[docs] https://docs.rs/tokio/latest/tokio/
まずはtokioの雰囲気を掴むために、リポジトリにあるサンプルコードを見ていきます
'hello_world.rs'が一番お手軽そうなので、そこから見ていきます
まずは、実行して動作をみました
コードの上部コメントにある指示通り実行。(macの場合はncat
をnc
に変更)
> nc -l 6142
この時点では何も表示されません
新しいターミナルセッションを立ち上げて、次のコマンドを実行します
> cargo run --example hello_world
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `/Users/sato-nobuaki/dev/roger-sato/sandbox/rust-study/tokio/target/debug/examples/hello_world`
created stream
wrote to stream; success=true
実行が終わると、ncを実行していたターミナルの方にhello worldが表示されました
> nc -l 6142
hello world
早速サンプルコードを見ていきます
#![warn(rust_2018_idioms)]
macro attributeでrustの古い文法に対して警告を出すようにしているみたいです
こんなことができるのですね
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
use std::error::Error;
コードで使用するライブラリを読み込んでいます
#[tokio::main]
main関数の前に置かれているこれが何かわかりません
とりあえず公式の説明
Marks async function to be executed by the selected runtime. This macro helps set up a Runtime without requiring the user to use Runtime or Builder directly.
"選択した非同期関数を指定することで、RuntimeやBuilderなしでRuntimeをセットアップできる"と書いてありますが、何を言っているのか依然としてわかりません
公式にRuntimeとBuilderの説明があったので読んでみます
まずは、Runtimeから
The Tokio runtime.
The runtime provides an I/O driver, task scheduler, timer, and blocking pool, necessary for running asynchronous tasks.
Instances of Runtime can be created using new, or Builder. However, most users will use the #[tokio::main] annotation on their entry point instead.
I/Oドライバ、タスクスケジュールなどのtokioを動かすための基盤となる機能を提供しているのですね
Builderやnewで作ることもできますが、#[tokio:main]
でも使えると書いています
同時にBuilderのイメージも大体掴みましたが、念の為説明を読んでみます
Builds Tokio Runtime with custom configuration values.
Methods can be chained in order to set the configuration values. The Runtime is constructed by calling build.
New instances of Builder are obtained via Builder::new_multi_thread or Builder::new_current_thread.
See function level documentation for details on the various configuration settings.
Builderパターンを利用してRuntimeを作成できるということですね
ちなみにサンプルもありました
use tokio::runtime::Builder;
fn main() {
// build runtime
let runtime = Builder::new_multi_thread()
.worker_threads(4)
.thread_name("my-custom-name")
.thread_stack_size(3 * 1024 * 1024)
.build()
.unwrap();
// use runtime ...
}
Runtimeを作成すると聞くと難しそうなイメージですが、思ったよりもわかりやすいですね
Runtimeの意味がわかったので、次は#[tokio::main]
の実装を見ていこうと思います
cfg_rt! {
#[cfg(feature = "rt-multi-thread")]
#[cfg(not(test))] // Work around for rust-lang/rust#62127
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
#[doc(inline)]
pub use tokio_macros::main;
#[cfg(feature = "rt-multi-thread")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
#[doc(inline)]
pub use tokio_macros::test;
cfg_not_rt_multi_thread! {
#[cfg(not(test))] // Work around for rust-lang/rust#62127
#[doc(inline)]
pub use tokio_macros::main_rt as main;
#[doc(inline)]
pub use tokio_macros::test_rt as test;
}
}
ここのコードのpub use tokio_macros::main;
が読み出しもとっぽいですね
#[cfg(not(test))] // Work around for rust-lang/rust#62127
の部分に書いてあるコメントが気になったので、調べてみました
どうやら、testコードでmainという識別子のproc macroを実行すると名前衝突が発生してしまうそうです
この回避策として、testをターゲットするときのコンパイルではここを読み込まないようにしてるんですね