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を触って、意味を予想しながら分からない所をググっていきました。 今人的には、作業としては「逐一調べる」より「一通り読む」のほうが好きなんだけど、 たまには、こういうのも楽しい。分からない所は分かった時点で追記していけたらと思います。