読者です 読者をやめる 読者になる 読者になる

(-> % read write unlearn)

All opinions expressed are solely my own and do not express the views or opinions of my employer.

rust-lang#00 Rust & Iron web framework に入門した

rust

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();

ここらへんから、一気に分かりません。

Ironnewし、その引数に無名関数を渡しています。 リターンされたIronインスタンスhttpメソッドを呼び、さらにメソッドチェーンでunwrapを呼んでいます。

Ironというstructの宣言はここで定義されてるようだが、実装自体はこっちに分かれているのでしょうか。。 うーん、よく分からない。。 newの実装はここでしょう。 handlerを受け取ってIron<H>型のstructをreturnするって宣言しているように見えます。 実装は1行でIron構造体の3つのフィールドを初期化しています。

Ironモジュール自体はuseしていないけど、ルートモジュールだから、externuseの効果も持ってるのかもしれませんね。

|_: &mut Request|
  • この縦棒は慣れないので結構違和感あるけど、Closureの構文です。
  • _は、無名関数(Closure)の仮引数名です。これは「使用しない」という意味の慣用的な命名なんだと思います。*1
  • Requestモジュールはiron::preludeの中でre-exportされてるので、修飾なしで使えています。
  • &mut Tは可変の参照で*mut Tとなることを強制され、&Tは不変の参照で*const Tになることを強制される、とあります。stackoverflow
    &が参照の宣言で、mutがmutableの宣言で、あわさって書かれているってことかな。。
    でも、リクエストの内容書き換えられちゃっていいでしょうか。。うーん。。
        Ok(Response::with((status::Ok, "Hello World!")))

Response::withシグネチャfn with<M: Modifier<Response>>(m: M) -> Responseなんですが、なぜそこに(status::Ok, "Hello World!")というのが渡せるのか、よく分からないです。。。

github

まとめ

おれおれOSを実装するつもりでRustに入門し、なぜかWebApplicationをハローワールドしました。 新しい技術や言語を勉強するとき、いつもだったらdocumentsやtutorialを一通り読む所から始めるのだけれど、今回はいきなりWebFrameworkを触って、意味を予想しながら分からない所をググっていきました。 今人的には、作業としては「逐一調べる」より「一通り読む」のほうが好きなんだけど、 たまには、こういうのも楽しい。分からない所は分かった時点で追記していけたらと思います。

*1:Clojureでも使用しない変数名に _ を使うし。