rust-lang#00 Rust & Iron web framework に入門した
System Programing
プログラミングを始めて7年くらい経ちます。気付けば、プログラミング書き始めの頃に頭に描いていた、おれおれOSを書いたりおれおれRDBを実装したりといった「これこそPrograming」というような開発ができる実力が付く気配が一向にありません。
で、いろいろいろいろあって(省略)、OSというものを自分達で書いてみよう、という話に友人となりました。
C言語でOS書いてみようと思って調べました。 そうしたら、C++のほうがオブジェクト指向でClassとかあるし、言語もどんどん発展してるし向いてるそうです。 で、C++でOSを書いてみようと思って調べました。 そうしたら、最近のシステムプログラミングはGoいけてるそうです。DockerもGoなのは有名ですね。 で、GoでOSを書いてみようと思って調べました。 そうしたら、D言語・・・(以下省略) 紆余曲折あってRustにたどり着きました。
以下の点で、ほとんど書いたことないのに(勝手に)Rustを気に入ったことにしました。
- OwnershipとかBorrowing、Lifetimeといった自分が全然知らなく、他の言語でもあまり聞いたことがない概念がたくさん出てくる。
- イケイケのGoと伝統のC++という競合(?)がいる中で、支持してる人が結構いるっぽい。
- モダン(らしー)
Web Programing in Rust
RustのSyntaxは前(1.0出る直前くらいだったか)に少し勉強したけど、もう少し勉強したいのでWebProgramingをしようと思います。 Webのほうがイメージ湧きやすいんじゃないかと思って。
RustでメジャーなWebFrameworkは、
- Iron: 古参かつメジャー。modularな構成で、clojureのringみたいなmiddlewareパターンのようです。
- Nickel: Node.jsのexpress inspired。
- Rustful: 軽量。REST-ful。
- Hyper: HTTPの低レイヤのAPIを提供するライブラリ。上記3つともこのHyperに依存しています。
libs.rsっていうサイトにもっと載ってます。 https://github.com/flosse/rust-web-framework-comparison#comparison には比較が載ってました。
パッと見、Ironのソースが一番よく分からなかったので、Ironに入門してみました。
rustup
以下、rustupが入っている前提です。
複数バージョンのrust環境の切り替えなどは、最近はmultirustではなくrustup
を使うみたいです。multirust
のREADMEに
Note: multirust is not actively maintained. Migrate to rustup.rs.
と書いてありました。Rustは6週間に1回stableリリースするらしいので、アップデートが頻繁です。楽をするためにもrustup
を入れておくと良いです。
rustup
を入れるとcargo
も入ります。
cargo
はrustのパッケージ管理&ビルドのツールです。mavenやleinigenやsbtやgradleやnpmみたいなのです。
rustup
のインストールはcurl https://sh.rustup.rs -sSf | sh
だけでOKです。より詳細な環境構築についてはRustをはじめよう! Rustの環境構築 Atom & SublimeText - Qiitaが詳しかったです。
new project
プロジェクトの作成
cargo new hi_iron --bin cd hi_iron
以下のような構成が出来上がります。
➜ hi_iron git:(master) ✗ tree . ├── Cargo.toml └── src └── main.rs 1 directory, 2 files
--bin
というオプションは、「ライブラリではなく直接実行するバイナリのプロジェクトである」ことを意味しています。
--bin
なしだと、例えばmain.rs
はなくてlib.rs
というのが作られるといった違いがあります。
main.rs
の中身は、こんな感じです。
fn main() { println!("Hello, world!"); }
さっそくcargo run
で実行してみます。
➜ hi_iron git:(master) ✗ cargo run Compiling hi_iron v0.1.0 (file:///Users/hata/ws/rs/hi_iron) Running `target/debug/hi_iron` Hello, world!
iron
つづいて、ironのドキュメントを見ながら、ブラウザでハローワールドしていきます。
cargo.toml
という設定ファイルの[dependencies]
にironの依存iron = "0.3.0"
を追記します。
例えば以下のようになります。
[package] name = "hi_iron" version = "0.1.0" authors = ["hata"] [dependencies] iron = "0.3.0"
main.rs
を書き換えます。
extern crate iron; use iron::prelude::*; use iron::status; fn main() { Iron::new(|_: &mut Request| { Ok(Response::with((status::Ok, "Hello World!"))) }).http("localhost:3000").unwrap(); }
内容はさておき、さっそくまた実行してみます。cargo run
します。
依存関係を解決しいてく際にコンパイルしたりするので、ちょっと時間がかかります。
Running target/debug/hi_iron
とか出たらコンパイル完了です。
http://localhost:3000/ を開いてHello World!
が表示されれば成功です。やったー!
iron main
とりあえず、ironをハローワールドできたけど、コードが分からないことだらけなので、少しづつ調べていってみます。
extern crate iron;
crate
は、ライブラリやパッケージのことです。crate
の中には複数のモジュールが入っています。extern
は、指定されたcrate
をコンパイルしてリンクせよという命令です。
なので、ここではironというcrate
(パッケージ)をextern
(コンパイルしてリンク)するという宣言になります。
use iron::prelude::*; use iron::status;
use
は、モジュールを現在のスコープ(ローカルスコープ)にインポートする宣言です。
ここでは、手前でリンクしたironパッケージから、いくつかのstatus
モジュールとprelude
名前空間直下の全てのモジュールをインポートしています。
fn main() { // 省略 }
fn
は、関数を宣言します。ここで見る分には他の言語とそんなに違いないですね。
Iron::new(|_: &mut Request| { // 省略 }).http("localhost:3000").unwrap();
ここらへんから、一気に分かりません。
Iron
をnew
し、その引数に無名関数を渡しています。
リターンされたIron
インスタンスのhttp
メソッドを呼び、さらにメソッドチェーンでunwrap
を呼んでいます。
Iron
というstruct
の宣言はここで定義されてるようだが、実装自体はこっちに分かれているのでしょうか。。
うーん、よく分からない。。
new
の実装はここでしょう。
handlerを受け取ってIron<H>
型のstruct
をreturnするって宣言しているように見えます。
実装は1行でIron
構造体の3つのフィールドを初期化しています。
Iron
モジュール自体はuse
していないけど、ルートモジュールだから、extern
がuse
の効果も持ってるのかもしれませんね。
|_: &mut Request|
- この縦棒は慣れないので結構違和感あるけど、Closureの構文です。
_
は、無名関数(Closure)の仮引数名です。これは「使用しない」という意味の慣用的な命名なんだと思います。*1Request
モジュールはiron::preludeの中でre-exportされてるので、修飾なしで使えています。&mut T
は可変の参照で*mut T
となることを強制され、&T
は不変の参照で*const T
になることを強制される、とあります。stackoverflow。
&
が参照の宣言で、mut
がmutableの宣言で、あわさって書かれているってことかな。。
でも、リクエストの内容書き換えられちゃっていいでしょうか。。うーん。。
Ok(Response::with((status::Ok, "Hello World!")))
- 最初の
Ok
は組み込みのModule std::result。
関数の実行結果を正常系と準正常系に簡単に分けるのに、rustではこのresult
をよく使うようです。 Response
モジュールもRequest
同様、iron::preludeの中でre-exportされています。Response::with
関数は、Response
型を返すコンストラクタです。status::Ok
はHTTPのステータスコードに対応した値を持つstatus
のEnum
値です。
Response::with
のシグネチャはfn with<M: Modifier<Response>>(m: M) -> Response
なんですが、なぜそこに(status::Ok, "Hello World!")
というのが渡せるのか、よく分からないです。。。
まとめ
おれおれOSを実装するつもりでRustに入門し、なぜかWebApplicationをハローワールドしました。 新しい技術や言語を勉強するとき、いつもだったらdocumentsやtutorialを一通り読む所から始めるのだけれど、今回はいきなりWebFrameworkを触って、意味を予想しながら分からない所をググっていきました。 今人的には、作業としては「逐一調べる」より「一通り読む」のほうが好きなんだけど、 たまには、こういうのも楽しい。分からない所は分かった時点で追記していけたらと思います。