Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ビルダー

説明

ビルダーヘルパーへの呼び出しによってオブジェクトを構築します。

#![allow(unused)]
fn main() {
#[derive(Debug, PartialEq)]
pub struct Foo {
    // 複雑なフィールドがたくさんある。
    bar: String,
}

impl Foo {
    // このメソッドはユーザーがビルダーを見つけるのに役立つ
    pub fn builder() -> FooBuilder {
        FooBuilder::default()
    }
}

#[derive(Default)]
pub struct FooBuilder {
    // おそらく任意のフィールドがたくさんある。
    bar: String,
}

impl FooBuilder {
    pub fn new(/* ... */) -> FooBuilder {
        // Foo に最低限必要なフィールドを設定する。
        FooBuilder {
            bar: String::from("X"),
        }
    }

    pub fn name(mut self, bar: String) -> FooBuilder {
        // ビルダー自体に名前を設定し、ビルダーを値として返す。
        self.bar = bar;
        self
    }

    // ここで Builder を消費せずに済ませられるなら、それは利点である。
    // つまり、FooBuilder を多数の Foo を構築するためのテンプレートとして
    // 使用できる。
    pub fn build(self) -> Foo {
        // FooBuilder から Foo を作成し、FooBuilder 内のすべての設定を
        // Foo に適用する。
        Foo { bar: self.bar }
    }
}

#[test]
fn builder_test() {
    let foo = Foo {
        bar: String::from("Y"),
    };
    let foo_from_builder: Foo = FooBuilder::new().name(String::from("Y")).build();
    assert_eq!(foo, foo_from_builder);
}
}

動機

そうでなければ多くのコンストラクターが必要になる場合や、構築に副作用がある場合に有用です。

利点

構築用のメソッドを他のメソッドから分離します。

コンストラクターの増殖を防ぎます。

1 行での初期化にも、より複雑な構築にも使用できます。

対象の構造体に新しいフィールドを追加する場合、クライアントコードの後方互換性を維持したままビルダーを更新できます。

欠点

構造体オブジェクトを直接作成する場合や、単純なコンストラクター関数よりも複雑です。

議論

Rust にはオーバーロードや関数パラメーターのデフォルト値がないため、このパターンは他の多くの言語よりも Rust で(そしてより単純なオブジェクトに対して)頻繁に見られます。ある名前を持つメソッドは 1 つしか持てないため、複数のコンストラクターを持つことは、Rust では C++、Java、その他の言語ほど扱いやすくありません。

このパターンは、ビルダーオブジェクトが単なるビルダーではなく、それ自体で有用な場合によく使用されます。たとえば、 std::process::CommandChild(プロセス) のビルダーです。このような場合、TTBuilder という命名パターンは使用されません。

この例では、ビルダーを値として受け取り、値として返します。ビルダーを可変参照として受け取り、返す方が、多くの場合よりエルゴノミック(かつより効率的)です。借用チェッカーにより、これは自然に機能します。このアプローチには、次のようなコードを書けるという利点があります。

let mut fb = FooBuilder::new();
fb.a();
fb.b();
let f = fb.build();

また、FooBuilder::new().a().b().build() スタイルも使用できます。

関連項目