🦀

100日後にRustをちょっと知ってる人になる: [Day 93]書籍: Webアプリ開発で学ぶRust言語入門 その3

2022/12/22に公開

Day 93 のテーマ

Day 91 から読み始めた Webアプリ開発で学ぶ Rust言語入門 のですが、今日も少しずつ読み進めようと思います。

  • 第 1 章 RustとWeb開発
    • 1.1 Rustでの開発の準備
  • 第 2 章 Rust基礎
    • 2.1 変数とデータ型
    • 2.2 関数の実装
    • 2.3 制御構造
    • 2.4 所有権による安全性
    • 2.5 データ構造
    • 2.6 async/await
    • 2.7 クレートとモジュール
    • 2.8 テスト
    • 2.9 よく使うライブラリ
  • 第 3 章 axumを使ってhttpリクエストを処理する
  • 第 4 章 sqlxを使ってCRUDを実装する
    • 4.1 データベース基礎
    • 4.2 sqlxとは
    • 4.3 axumとsqlx
    • 4.4 todoのCRUD
    • 4.5 sqlxのテスト
  • 第 5 章 Todoアプリの体裁を整える
    • 5.1 フロントエンド開発
    • 5.2 React環境構築
    • 5.3 TodoアプリのUI実装
    • 5.4 外部APIとの通信(1)
    • 5.5 外部APIとの通信(2)
  • 第 6 章 Todoにラベルをつける
    • 6.1 ラベルのCRUD
    • 6.2 TodoRepositoryのラベル対応
    • 6.3 ラベル機能を画面に追加する
    • 6.4 さらなる機能拡張

第 3 章 axumを使ってhttpリクエストを処理する - 3.4 Todo情報を保存する

3.4 章 からは、サンプルアプリケーションとして Todo アプリの開発をしながら Rust の学習を進めていく流れになっています。Todo アプリケーションは、その他の言語でも Web アプリケーションの題材としてよく扱われる内容なので、デザインについては想像できる人が多いのではないかなって思います。HTTP メソッドに対応した、CRUD 操作を紐付けていくという流れになりますよね。それを、Rust で実装する場合はどのようにしていくのかを、ぼくも学びたいと思います。

HTTP メソッド CRUD 操作
GET Create (作成)
POST Read (参照)
PUT Update (更新)
DELETE Delete (削除)

TodoRepository の作成

エラー用の enum

Debug と Error マクロを持つ Error 用の enum を作ります。ここでは、まずは NotFound のみを定義しています。

#[derive(Debug, Error)]
enum RepositoryError {
    #[error("NotFound, id is {0}")]
    NotFound(i32),
}

TodoRepository の振る舞いのためのトレイト

ここで、TodoRepository: Clone + std::marker::Send + std::marker::Sync + 'static として A + B のように記述しているのは、複数のトレイトを継承しているということです。

pub trait TodoRepository: Clone + std::marker::Send + std::marker::Sync + 'static {
    :
}

ここで継承したのは、次のトレイトです。この時点では、なぜこのトレイトを継承しているか分かりにくいですが、axum のお作法として Clone + Send + Sync + 'static を継承するようです。

そして、実際の振る舞いとしては次のように CRUD 操作を作っています。

pub trait TodoRepository: Clone + std::marker::Send + std::marker::Sync + 'static {
    fn create(&self, payload: CreateTodo) -> Todo;
    fn find(&self, id: i32) -> Option<Todo>;
    fn all(&self,) -> Vec<Todo>;
    fn update(&self, id: i32, payload: UpdateTodo) -> anyhow::Result<Todo>;
    fn delete(&self, id: i32) -> anyhow::Result<()>;
}

データベースの代用としての HashMap

HashMap は複数スレッドからアクセスされる可能性を考えてスレッドセーフにする必要があります。そこで、store: Arc<RwLock<TodoData>> としてスレッドセーフに書き換えています。Arc<RwLock<TodoData>> は不変参照のときには複数スレッドで共有しますが、可変参照のときは RwLock がスレッドを 1 つに制限するために安全な書き込みをすることが可能です。

type TodoData = HashMap<i32, Todo>;

#[derive(Debug, Clone)]
pub struct TodoRepositoryForMemory {
    store: Arc<RwLock<TodoData>>,
}

impl TodoRepositoryForMemory {
    pub fn new() -> Self {
        TodoRepositoryForMemory {
            store: Arc::default(),
        }
    }   
}

未実装のためのマクロ

今日はここまでとしようと思うときに、ビルドは通しておきたいけど実装ができてないメソッドがあったりしますよね。そのようなときには、todo!マクロ で未実装にしておくことができます。

というわけで、CRUD 操作に関するところは今日はもう実装しないで todo マクロを使っておきます。

impl TodoRepository for TodoRepositoryForMemory {
    fn create(&self, payload: CreateTodo) -> Todo {
        todo!();
    }

    fn find(&self, id: i32) -> Option<Todo> {
        todo!();
    }

    fn all(&self) -> Vec<Todo> {
        todo!();
    }
    :
    :
}

続きはまた明日。

Day 93 のまとめ

axum を使った Web アプリケーションの作り方を見ていっていますが、少しずつ axum の特徴というか癖というか書き方のポイントみたいなもの見つけ始めた気がします。
まだ実際のリポジトリの処理の実装は全然できていないので、明日以降見ていきたいと思います。

GitHubで編集を提案

Discussion