0Pricing
Learn Rust Coding · レッスン

型状態Builder

型システムに有効性を組み込みます。

「型状態Builder」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。

通常のビルダーが抱える問題

通常のビルダーでは、必須フィールドを設定する前でも、いつでもbuild()を呼び出せます。その場合、データの不足は実行時のpanicやErrとして表面化します。

型状態ビルダーでは、このチェックをコンパイル時に行います。必須の手順を忘れると、コンパイルに失敗します。

型に状態をエンコードする

ポイントは、完了した手順を表すマーカー型を使って、ビルダーをジェネリックにすることです。各フィールドを設定すると、ビルダーの型が変化します。

すべての必須マーカーが"set"状態になったときだけ、build()メソッドが利用可能になります。

struct Missing;
struct Set;

マーカーに対してジェネリックなビルダー

必須フィールドごとの状態を表す型パラメーターを、ビルダーに持たせます。PhantomDataは、実際のデータを格納せずにマーカーを保持します。

use std::marker::PhantomData;
struct ReqBuilder<H, U> {
    url: Option<String>,
    method: Option<String>,
    _state: PhantomData<(H, U)>,
}

開始状態

コンストラクターは、すべての必須マーカーがMissingになっているビルダーを返します。この時点ではbuild()が存在しないため、型システムはオブジェクトが未完成であることを認識できます。

impl ReqBuilder<Missing, Missing> {
    fn new() -> Self {
        ReqBuilder { url: None, method: None, _state: PhantomData }
    }
}

マーカーを遷移させる

セッターは古いビルダーを消費し、そのフィールドのマーカーをSetに変更した新しいビルダーを返します。もう一方のマーカーは、その型パラメーターをジェネリックのままにすることで保持されます。

impl<U> ReqBuilder<Missing, U> {
    fn url(self, url: &str) -> ReqBuilder<Set, U> {
        ReqBuilder { url: Some(url.to_string()),
            method: self.method, _state: PhantomData }
    }
}

2つ目の遷移

メソッドを設定する場合も同じです。2つ目のマーカーをMissingからSetに変更し、1つ目のマーカーはそのままにします。

impl<H> ReqBuilder<H, Missing> {
    fn method(self, m: &str) -> ReqBuilder<H, Set> {
        ReqBuilder { url: self.url,
            method: Some(m.to_string()), _state: PhantomData }
    }
}

完全に設定されたときだけbuild()を使える

重要なのは、build()がReqBuilder<Set, Set>に対してのみ実装されていることです。それ以外の状態にはこのメソッドが存在しないため、呼び出すとコンパイルに失敗します。

内部のunwrap呼び出しがpanicすることはありません。型によって、両方のフィールドが存在することが保証されているためです。

struct Request { url: String, method: String }
impl ReqBuilder<Set, Set> {
    fn build(self) -> Request {
        Request { url: self.url.unwrap(), method: self.method.unwrap() }
    }
}

全体を組み合わせる

正しいチェーンは、各呼び出しによってビルダーがbuild()の存在するReqBuilder<Set, Set>へ近づくため、問題なくコンパイルできます。

fn demo() -> Request {
    ReqBuilder::new()
        .url("https://example.com")
        .method("GET")
        .build()
}

望ましいコンパイルエラー

必須の手順を省略すると、コンパイラーが拒否します。ReqBuilder<Set, Missing>でbuild()を呼び出すと「buildという名前のメソッドがない」と報告され、プログラムを実行する前に手順の抜けを検出できます。

// ReqBuilder::new().url("x").build();
// error: no method named `build` found for
// ReqBuilder<Set, Missing>

単独で実行できるバージョン

この最小限のプログラムでは、必須フィールドを1つだけ使い、遷移全体を最初から最後まで示します。コンパイルに成功し、構築した値を出力します。

use std::marker::PhantomData;
struct Missing; struct Set;
struct B<N> { name: Option<String>, _s: PhantomData<N> }
impl B<Missing> {
    fn new() -> Self { B { name: None, _s: PhantomData } }
    fn name(self, n: &str) -> B<Set> {
        B { name: Some(n.to_string()), _s: PhantomData }
    }
}
impl B<Set> {
    fn build(self) -> String { self.name.unwrap() }
}
fn main() {
    let v = B::new().name("prod").build();
    println!("built: {}", v);
}

コストとトレードオフ

型状態ビルダーでは、マーカーが消去されるため、実行時オーバーヘッドなしでコンパイル時の保証を得られます。一方、必須フィールドが増えるほど、型の仕組みが複雑になり、組み合わせごとのimplブロックも増えます。

不完全な構築を仕組み上不可能にする必要があるAPIで、このパターンを使用してください。

理解度チェック

型状態ビルダーで完全性を実際に保証しているものを確認しましょう。

まとめ

型状態ビルダーでは、必須の各手順をマーカー型パラメーターとしてエンコードし、PhantomDataで保持します。セッターはビルダーを消費し、1つのマーカーをSetに変更した新しい型を返します。

build()はすべてがSetの状態に対してのみ実装されているため、手順を忘れると実行時のpanicではなくコンパイルエラーになります。しかも実行時コストはかかりません。

よくある質問

「型状態Builder」レッスンは無料ですか?

はい。「型状態Builder」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。

「型状態Builder」で何を学びますか?

型システムに有効性を組み込みます。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「型状態Builder」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Builderパターン
  2. Newtypeパターン
  3. 型状態Builder
  4. Derefとラッパーの使いやすさ
← Learn Rust Codingに戻る