axum 0.6 の開発環境を Docker で構築、公式サンプルコードの内容把握
はじめに
本記事では Rust の Web アプリフレームワーク axum 0.6 の開発環境を Docker を使って構築する方法と、サンプルコードを理解するために調べたことをご紹介します。
今回のファイル構成は下記のようになっていて、apps フォルダが Docker コンテナの /var/rust と同期されています。
/path/to/your-project/
├ apps/
│ └ axum-app/
│ ├ src/
│ │ └ main.rs
│ └ Cargo.toml
│
└ docker/
└ docker-compose.yml
- OS
- Windows 11 Home
- Docker
- Docker Desktop 4.23.0
- Rust
- rustc 1.72.1
1. Docker 環境を構築
ベースは先日の「Rust の学習環境を Docker と VSCode で構築」で紹介したもので、違いは ports でポートマッピングを追加しただけです。
version: "3"
services:
rust-app:
container_name: rust-app
image: rust:latest
ports:
- 3000:3000
volumes:
- ../apps:/var/rust
working_dir: /var/rust
tty: true
image で latest を指定しているので、最新版の Rust で axum 0.6 が動作しない等の場合はバージョン番号を指定してみると良いかもしれません。
(今回は 1.72.1 を使用しています)
2. axum 0.6 でプログラムを作成
axum の公式サイトで紹介されている下記コードを使ってサンプルプログラムを作成します。
- Example (axum - Rust)
- https://docs.rs/axum/latest/axum/#example
Docker のコンテナに入り、下記コマンドで新規プロジェクトを作成します。
axum-app は任意の名称で OK です。
root@0123456789ab:/var/rust# cargo new axum-app
Created binary (application) `axum-app` package
次に Cargo.toml の [dependencies] に下記のように追加します。
[package]
name = "axum-app"
version = "0.1.0"
edition = "2021"
[dependencies]
# ↓ これらを追加
axum = "0.6.20"
hyper = { version = "0.14.27", features = ["full"] }
tokio = { version = "1.32.0", features = ["full"] }
tower = "0.4.13"
- Required dependencies (axum - Rust)
- https://docs.rs/axum/latest/axum/#required-dependencies
各バージョン番号は下記 GitHub のページを参考に、作成時点での最新版を記載しました。
- Releases · tokio-rs/axum · GitHub
- https://github.com/tokio-rs/axum/releases
- Releases · hyperium/hyper · GitHub
- https://github.com/hyperium/hyper/releases
- Releases · tokio-rs/tokio · GitHub
- https://github.com/tokio-rs/tokio/releases
- Releases · tower-rs/tower · GitHub
- https://github.com/tower-rs/tower/releases
そして main.rs を下記内容に変更します。
use axum::{
routing::get,
Router,
};
#[tokio::main]
async fn main() {
// ルーティング設定
let app = Router::new()
.route("/", get(|| async { "Hello, world!" }));
// サーバを起動
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
あとは axum-app 内で cargo run コマンドで実行です。
下記のようになれば正常完了です。
root@0123456789ab:/var/rust# cd axum-app
root@0123456789ab:/var/rust/axum-app# cargo run
Updating crates.io index
(省略)
Running `target/debug/axum-app`
ブラウザで http://localhost:3000 にアクセスすると「Hello, world!」と表示されます。
3. サンプルコードの内容把握
まずは下記のルーティング部分。
let app = Router::new()
.route("/", get(|| async { "Hello, world!" }));
route() の第1引数 "/" は URLで、第2引数の get() はそこに GET メソッドでアクセスしたときに行う処理を表しています。
get() が取る引数は関数で、指定されている || async { "Hello, world!" } はクロージャです。("Hello, world!" を返しています)
- route() (Router in axum::routing - Rust)
- https://docs.rs/axum/latest/axum/routing/struct.Router.html#method.route
- get() (MethodRouter in axum::routing::method_routing - Rust)
- https://docs.rs/axum/latest/axum/routing/method_routing/struct.MethodRouter.html#method.get
- Closure Type Inference and Annotation (Closures: Anonymous Functions that Capture Their Environment - The Rust Programming Language)
- https://doc.rust-lang.org/book/ch13-01-closures.html#closure-type-inference-and-annotation
次にサーバー起動部分。
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
bind() はサーバのアドレスを設定する関数で、引数は &SocketAddr になります。
その引数 "0.0.0.0:3000".parse().unwrap() ですが、これは文字列スライス "0.0.0.0:3000" を SocketAddr に変換しています。
parse().unwrap() は、変換したい型が FromStr トレイトを実装している場合に、その型に変換することができます。
今回の場合は SocketAddr に FromStr が実装(インプリメント)されているで、これで型変換になるんですね。
- bind() (Server in hyper::server - Rust)
- https://docs.rs/hyper/0.14.27/hyper/server/struct.Server.html#method.bind
- parse() (str - Rust)
- https://doc.rust-lang.org/stable/std/primitive.str.html#method.parse
- Trait std::str::FromStr (FromStr in std::str - Rust)
- https://doc.rust-lang.org/stable/std/str/trait.FromStr.html
serve() はサーバを生成するもので、ルーティング設定を渡すために app を into_make_service() 関数で IntoMakeService に変換しています。
このサーバ生成はあくまで非同期タスクとして作成するだけなので、.await をつけて実行させているようです。
await の後に unwrap() しているのはエラー時に panic (クラッシュ) させるためでしょうか。
- serve() (Builder in hyper::server - Rust)
- https://docs.rs/hyper/latest/hyper/server/struct.Builder.html#method.serve
- into_make_service() (Router in axum - Rust)
- https://docs.rs/axum/latest/axum/struct.Router.html#method.into_make_service
4. おわりに
以前の投稿にも書きましたが、今 Rust の学習を書籍「Webアプリ開発で学ぶ Rust 言語入門」を読みながら進めています。
- Webアプリ開発で学ぶ Rust言語入門 - 秀和システム あなたの学びをサポート!
- https://www.shuwasystem.co.jp/book/9784798067315.html
この本にある axum のサンプルコードは上記とは若干異なるのですが Route や SocketAddr 等の説明があって理解の手助けになりました。
Rust はネット上にも多くの資料がありますが、特に英語が苦手な場合はこういった入門書があると学習効率が上がるかもしれません。
ただし書籍だけでは情報量が不十分だったり、バージョンが古かったりするので、より理解を深めるために公式ドキュメントや他資料も参照しならが学習を進めるのが大切と考えています。