Rのつく財団入り口

ITエンジニア関連の様々な話題を書いているはずのブログです。

『Webアプリ開発で学ぶ Rust言語入門』:React+RustでToDoアプリを作ろう

Rust言語のWeb開発特化本登場

 最近新刊が多いRust言語の本に、Web開発特化本まで!ということで読んでみたので読書記録です。

第1章 RustとWeb開発

作者の佐藤昭文さんはzennでご活躍中のフロントエンド界の方。そもそもこの記事がきっかけで本書が生まれたという話も冒頭で語られています。

zenn.dev zenn.dev

うーん懐かしい。2020年時点であの記事は僕も拝見していました。確かにフロントエンドの将来を展望する上でWASMだ、そしてそれを実装する言語ならRustが第一候補だというのはよく語られています。Rustが熱いかもしれないというところから始まって本書の企画になったのですねえ。

 そして序盤は2022年の最新状況を踏まえつつ簡潔に、QiitaやZennの記事の延長っぽい感じで、言語仕様の説明はシンプルめに進んでいきます。

第2章 Rust基礎

 こちらも説明はシンプルめに、簡単なコード実例を交えて進みます。
後半のトレイトのところで、関数の引数の型の指定で「トレイト境界」という和訳は今後変わるかもしれないというのはへえーと思いました。それぞれの引数に複数のトレイト実装を強制するときは+で足し算のようにも書けるんですよね。あまりケースは少ないかもしれないですが適切に使うと力を発揮する場面もありそうです。
 このインターフェースの力を活用したコード設計の話では、お馴染み書籍『Clean Architecture』の名前も出てきます。

iwasiman.hatenablog.com

JavaScript経験者を想定して非同期のasync/awaitを解説。非同期ランタイムのtokioも名前だけ出てきます。
作成物の単位は

パッケージ:機能を提供する単位でルートにCargo.tomlあり
>クレート:コンパイルの単位でライブラリクレートとバイナリクレート
>モジュール:コードをグループ化、他の言語のパッケージや名前空間に相当

 ここは他の言語と微妙に違ってややこしいですね...
主要ライブラリではanyhow/thiserrorJSONの取り扱いでよく出てくるSerdeも登場します。

第3章 axumを使ってhttpリクエストを処理する

 お題としてはフロントエンドの解説記事でも定番、CRUDがあって一通りのWebアプリ処理があるTODOアプリを作っていく章です。 RustでのバックエンドのWebアプリフレームワークは選択肢は4つほど。

  • rocket: 古参、シンプルで高速でセキュア
  • Actix-web: 古参、http2やWebsocketも使えて対応範囲広し、高パフォーマンス
  • warp: Filterという独自概念。前の2つより後発で徐々に伸びている
  • axum: 非同期ライブラリのtokio開発チーム製。マクロを一切使わない。tokioとの親和性や将来性で本書のイチオシ。

Rustの書籍や情報を見るとだいたいactix-webが出てくるのでそちらかなと思っていたのですが、やや意外でした。axumは「エイクサム」と読むのでしょうか?

github.com

 環境構築はaxum, 非同期処理用のtokio の他に各種パッケージを入れて簡単。HTTPリクエストを使うのに外部パッケージとしてhyper、サーバー構築周りにtowerが必要と、このあたりはGo言語に比べると言語の標準機能で揃えている範囲は狭くなっています。

 実装はmain関数の中にほぼ固定のお決まりのコードを書くともうサーバーが立ち上がるのは他のパッケージでの実現方法とだいたい同じ。
"/hogehoge"などのルートの指定と紐付けた別関数が動いてAPIごとの処理をしていく...というのも大まかには同じでしょうか。構造体をJSONと関連付けて処理できるのは便利です。
 そしてサーバ構築に使うtowerパッケージの機能を使って、テストコードはロジックの関数レベルのみならず、指定URLへのHTTPリクエストレベルでのテストまでも行えてコード例がしっかり載っています。ここまでやるのはけっこうコード量が増えて大変そうだな...と一見しては思ったのですが、実際の開発ではどうなのでしょうか。

 そしていよいよTodoアプリの実装。

  • 各種トレイトを実装したTodoRepositoryというトレイトが、CRUDの必要な5つの関数の実装を強制する親インターフェースのようなもの。
  • 最初はHashMapにデータ保持ということでTodoRepositoryForMemory構造体を実装。内部にaxumの仕組み経由でHashMapを保持、impl TodoRepository for TodoRepositoryForMemory としてCRUDに対応した各関数を実装。こちらの構造体がインターフェースを実装した子供の実装クラスみたいな感じ。

コンパイル時はチェックをパスし実際に呼ばれるとpanicを発生させるという todo!() マクロは初めて知りました。仮実装のときに使うのですね。

 途中からmain.rs内実装のmain() 関数から離れて、handler.rsファイル実装の中間層を経てRepository層に至る構造になっていきます。最初にテストコードを実装してから対象の関数を実装していくTDDスタイルを貫いているのは偉いです。

 バリデーションについてはaxumには付属していないそうで、実現方法は以下。

  • シンプルなパッケージのvalidatorを追加。構造体自体のderiveアトリビュートでValidateを指定、構造体の各フィールドへのアトリビュート追加の形で、URL形式とか電子メール形式とか最大値いくつとかを指定。すると構造体にvalidate()関数が実装される。
  • axumの仕組みの中でこれを使うため、FormRequestトレイトを利用、ValidatedJsonという構造体を別に用意、ジェネリクスを活用して行う。

このへん初見にはちょっと難しいなと思いました。

第4章 sqlxを使ってCRUDを実装する

 データはTodoRepositoryForMemoryを通してHashMapに保存していたところ、本章ではDocker内に構築したPostgreSQLのテーブル内に保存する形に実装を進めていきます。現在RustのSQL関係のライブラリで有力なのは2つ。

  • diesel: ORMかつクエリービルダー。Rustの構造体のメソッド呼び出しでSQLを書かずともデータ取得ができたり。人気があるがマイグレーションSQLで書く。多機能だがasyncに非対応。RailsやLaravel内臓の機能と同じような感じで、注意しないとN+1問題が起きたりするのも同じ。
  • sqlx: シンプルでコンパイル時のSQLチェックやマイグレーションを行う。非同期処理に対応。SQL文を書くスタイル。

github.com

Rustの書籍だとだいたいdieselが紹介されているイメージですが、本書ではシンプルなsqlxを使っていきます。インストール後にマイグレーションのコマンドを打ってテーブルを作っていき、ここはだいたい他のORM系ライブラリと同じ。

  • 非同期対応なのでDBまで至る各関数もasyncをつけるように改造
  • TodoRepositoryForDb 構造体を実装。フィールドにsqlxが提供するsqlx::PgPool型を持ち、CRUDの各関数をasyncで実装
  • 最初のmain()関数で全体が立ち上がる際、環境変数から設定を読み取ってこのTodoRepositoryForDbが生成されるように追加
  • これまで使ってきたTodoRepositoryForMemoryは消さずに、テスト時に使うように移動
  • 実際のDBアクセスは、sqlx::query_as など指定の関数の引数にSQL文を書き、追加で更に呼ぶ関数で条件をバインドしたり。
  • テストはTodoRepositoryForMemoryを使わず実際にDBアクセスする場合の例も記述
  • featureという外部パッケージを使うと、テストコードのアトリビュートで指定した機能だけテストしたりができる。

 sqlxは確かにシンプルでDB周りが分かっている人にはこちらでも十分な気がしました。逆に生のSQL文をRustコード内に書く方式よりも優れている差分はどこなのかな、とも思いました。

第5章 Todoアプリの体裁を整える

 ここまででWeb APIとしてTodoのCRUDはすべて揃ったので画面を作ろう...ということで、まるごと一章使ってフロントエンドの章になります。
2022年の技術選定ではReactとVue.jsの2択。Vueのほうが学習コストが低いという意見もありますが作者さんはReact派のようですね。同じく関数型言語由来の特徴がReactにもRustにもあり、RustプログラマーにはReactの方が受け入れられやすいだろう...という意見はわかる気がします。

  • いつも通りNode.jsとnpmを使用。
  • プロジェクト作成はcreate-react-appでなく、2022年現在勢いのあるviteを使用
  • 言語はJSでなくTSで、現在主流の関数コンポーネントで実装。React hooksも使用。
  • ライブラリのmodern-css-resetを入れて、スタイルのリセットをして進行
  • コンポーネントは分割、バックエンドとの通信はapi/ ディレクトリに外出しにして実装...

 と、Rustの本なのにReactの実装例も学べるオトクな構成になっています。純粋にRustだけを学びたい方はこの章はスキップでも良いでしょう。
 Vue.js作者のEvanさんが作ったビルドツールのvite、React畑でもやっぱり支持されてるんですねー。

第6章 Todoにラベルをつける

 最後は機能拡張の例として、Todoひとつひとつにラベルがつけられるように。DBにテーブルを追加してTodoテーブル1レコードに複数行でラベルを持てるように...という設計で拡張していきます。

  • テーブルが増えるのでRepository層は src/repositories/todo.rs, labels.rs に分割。元のsrc/repositories.rs には共通のエラー定義をまとめる。
  • src/main.rs は同じ。Repository層の各テーブル用の構造体を最初に生成するように。
  • 中間のHandler層も src/handlers/todo.rs, labels.rsに分割。元のsrc/handlers.rs には共通の構造体をまとめる。
  • idとnameを持ったlabelsテーブル追加、todoテーブルとlabelsテーブルを紐付けるtodo_labelsテーブル追加。todo_labelsテーブルには、PostgreSQLの遅延制約の機能を使ってトランザクションのコミット時まで制約検証を遅延。
  • テーブルが増えて内部処理もだいぶ変わるので、テストコードを逐一実施しながら修正
  • フロントエンドのReact側もラベルを選べるように機能追加、こちらもだいぶ改造が入るので修正

 他にもTodoアプリの機能拡張の案は考えられるので、続きは自分でやってみてくださいという流れで修了です。

まとめ:React+Rust with axum, sqlx でモダンなWebアプリの実装例が一通り分かるWeb開発特化本

 zennやQiitaの記事の豪華版のような感じで、フロントエンド寄りの視点も交えつつ一通りのコード実装例が分かる本でした。
最初に仮実装してテストコードを書き、本実装してテストがすべて通るように...とTDDスタイルを貫いてテストコードも一緒に載っているのが良いですね。
Rustのトレイト、より一般的にはインターフェースを活用して機能拡張を前提としたコードになっており、後から既存コードを修正するスタイルで進んでいくのも現実に即していて良いです。(本で読むと+,-で示されたコードの差分はちょい見にくいのですが...)

 Rustの文法などもろもろ解説はありますが比較的あっさりめです。このへんはコード例がメインのWebアプリ開発特本の性質上仕方ないのではと思います。Rust言語自体に入門したい方は本書でなく最初は他の本、2冊め以降の参考で読むとよいかと思います。比較的ページ数も少なくお値段も2千円以内と安いので、軽い気持ちで手に取れます。

 axumフレームワークについては...まあ入門中なので大して語れないですが、Rust言語のマクロ機能を使わないという方針からなのか、提供されている独自の型やトレイトを使うシーンが多かったり独特なところは多めなのかな?という印象でした。(たぶん自分が素人だからでしょうが...)
同じく主流になるかもしれないと言われるActix-webとの対比視点ではどうなのかなども知りたいところですが、どこが覇権を取るのかも含め、このへんは今後の発展やWeb上の情報が増えてくるのを待ちたいと思います。

actix.rs

 2022年限定で本書含め商業本が計7冊も新刊が出たRust言語。今後の広がりが楽しみです。

www.rust-lang.org