Rustのwebフレームワーク"Rocket"のサンプルを解説してみる~hello_2018編~

Rustについて一通り学習してみたので、練習がてらアプリケーションでも自作してみようとフレームワークを探してみました。どうやら[*Rocket*](https://rocket.rs/)がいい感じっぽい。  
  
## Rocketとは  
Rustに対応する軽量webフレームワーク。公式のガイドは[こちら](https://rocket.rs/v0.4/guide/)。  
    
## サンプルコード  
*Rocket*はまだまだ新興フレームワークなこともあって、その他の主流なものと比較すると書籍や動画教材などのリソースが少ない。ですが、GitHubにて[公式のサンプルコード](https://github.com/SergioBenitez/Rocket/tree/v0.4/examples)が公開されています！ありがたや。。。 
サンプルコードでは*Rocket*の使い方を様々な実装例を元に学ぶことができます。今回は、`hello_2018 `の実装内容について解説してみます。    
  
## hello_2018の実装を見てみよう
まず、ディレクトリ構成はこんな感じになっています。  

>hello_2018
>├── Cargo.toml
>└── src
> &emsp;&emsp;&emsp;  ├── main.rs
> &emsp;&emsp;&emsp;   └── tests.rs
  
Cargo.tomlに関しては、コンフィグファイルのようなものなので割愛します。それでは早速実装を見ていきましょう。  
  
### main.rs   

```rust:main.rs

#![feature(proc_macro_hygiene)]

#[cfg(test)] mod tests;

use rocket::{get, routes};

#[get("/")]
fn hello() -> &'static str {
    "Hello, Rust 2018!"
}

fn main() {
    rocket::ignite().mount("/", routes![hello]).launch();
} 
```  
    
#### `#![feature(proc_macro_hygiene)]`  
不安定なRustの機能性をRocketで利用するためのフラグのようなものです。公式からもちゃんとしたドキュメントは提供されておらず、[関連するGitHub issue](https://github.com/rust-lang/rust/issues/54727)としてやり取りが公開されているのみです。。。
    
#### `#[cfg(test)] mod tests;`
`tests.rs`を読み込んでいる箇所です。`#[cfg(test)]`のアノテーションは`tests.rs`が`cargo test`が行われた場合にのみ実行・コンパイルされるようRocketに伝えています。こうすることで、`cargo build`などでコンパイルを行う際に所要時間が短縮される、といった利点があります。  
  
#### `use rocket::{get, routes};`  
`Cargo.toml`で読み込んでいる`rocket`から各種インポートします。  
  
#### `#[get("/")] fn hello()...`
「urlが"/"のGETリクエストがきた場合には"hello"ファンクションを実行してね」と伝えている箇所です。Rust自体の文法の話になってしまうのであまり深入りはしませんが、`-> &'static str`の部分では「"hello"ファンクションの返り値の型は"str"のリファレンスで、ライフタイムは"'static"になるよ」と言っています。  
  
#### `fn main()...`  
いわゆるmain関数部分です。`rocket::ignite()`でRocketのインスタンスを作成し、`mount("/", routes![hello])`で作成した`hello`ファンクションに対してbaseURLをマウントしています。今回では`mount("/"...`となっているため、baseURLは存在しませんが、もし`mount("hoge/"...`などとなっていた場合には、`hello`ファンクションに対応するURLは`hoge/`となります。`launch()`は単にサーバーを起動させ、設定したマウントURLに対してのリクエストを扱えるようにRocketに指示を出しています。  
  
### test.rs  
```rust:test.rs
use rocket::{self, routes, local::Client};

#[test]
fn hello_world() {
    let rocket = rocket::ignite().mount("/", routes![super::hello]);
    let client = Client::new(rocket).unwrap();
    let mut response = client.get("/").dispatch();
    assert_eq!(response.body_string(), Some("Hello, Rust 2018!".into()));
}  

// Tests unrelated to the example.
...
```  
11行目〜の箇所はmain.rsの実装とは関係のない箇所となっているので、今回は割愛します。 実装を見ていきましょう。  

#### `use rocket::{self, routes, local::Client};`  
`Cargo.toml`で読み込んでいる`rocket`から各種インポートします。  
  
#### `#[test]`  
このアノテーションをファンクションの前につけることで、Rocketに「このファンクションはテスト用だよ」と伝えています。こうすることで、`cargo test`でテストを実行すると自動的にファンクションを走らせてくれるようになります。  
  
#### `fn hello_world()...`  
実装が少し長いので、一つずつ見ていきましょう。まず、　　
`let rocket = rocket::ignite().mount("/", routes![super::hello]);`　　
の箇所は既に`main.rs`の解説で同様の箇所に触れているので割愛します。  
`let client = Client::new(rocket).unwrap();`　　
で、作成したrocketのインスタンを利用してローカルリクエストをハンドルするためのclientインスタンスを作成しています。`unwrap()`は`Client::new()`で返却されるResult型が`Ok`だった場合には中の値を返し、`Err`だった場合にはpanicを起こすメソッドです。今回では`Ok`が返るはずなので、`client`変数にはローカルリクエスト用のclientインスタンスが格納されます。  
`let mut response = client.get("/").dispatch();`　　
では「`client`を利用して"/"のURLにGETリクエストをディスパッチしてね」と指示を出しています。`mut`は「`response`変数はmutableにしてね」と宣言している箇所です(Rustでは変数はデフォルトでimmutable)。  
最後に,
`assert_eq!(response.body_string(), Some("Hello, Rust 2018!".into()));`  でリクエストが`main.rs`で想定したレスポンスを取得できているかをアサーションしています。`Some("Hello...)`となっているのは`body_string()`の返却型がOptionであるためで、`...2018!".into()`を用いているのは`&str`を`std::string::String`に変換するためです。  
  
## 所感   
Rustは少し癖のある言語で、Rocketについてもリソースが豊富とは言い難いので学習には苦戦しそうです。。。  
他にもたくさんのサンプルコードが提供されているので、またの機会に同様の記事がかけたらいいなと思います。