0Pricing
Learn Rust Coding · レッスン

Builderパターン

オブジェクトを段階的に構築します。

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

Builderを使う理由

Rustには、名前付き引数や省略可能な関数引数がありません。構造体のフィールドが多く、特に省略可能なフィールドがある場合、8個の位置引数を持つコンストラクターは読みにくく、間違いやすくなります。

この問題を解決するのがbuilderパターンです。名前付きメソッドでオブジェクトを段階的に設定し、最後にbuild()を呼び出して値を生成します。流れるような文章として読めます。

対象の構造体

まず、実際に構築したい型を用意します。ここでは、サーバー設定が必須のホストと、いくつかの省略可能な設定項目を持ちます。

フィールドが非公開なのは、構造体リテラルではなくbuilderを通して構築するよう促すためです。

pub struct ServerConfig {
    host: String,
    port: u16,
    max_connections: usize,
    use_tls: bool,
}

専用のBuilder型

典型的な方法では、builderという2つ目の構造体を使います。対象の構造体を写しつつ、構築途中の状態を保持します。省略可能なフィールドは、未設定と明示的な設定を区別できるように、Option<T>にすることがよくあります。

pub struct ServerConfigBuilder {
    host: String,
    port: Option<u16>,
    max_connections: Option<usize>,
    use_tls: bool,
}

Builderを開始する

builderには、必須フィールドだけを受け取るコンストラクターを用意します。省略可能なフィールドはすべて、Noneまたはデフォルト値で開始します。

対象の型にbuilder()メソッドを用意し、builderを返すのが一般的な慣習です。

impl ServerConfig {
    pub fn builder(host: impl Into<String>) -> ServerConfigBuilder {
        ServerConfigBuilder {
            host: host.into(),
            port: None,
            max_connections: None,
            use_tls: false,
        }
    }
}

Setterメソッドはselfを値で受け取る

各setterはselfを消費し、フィールドを変更してselfを返します。所有する値を返すことで、呼び出しを流れるようにつなげられます。

この所有権に基づくチェーンは、Rustらしい慣用的なスタイルであり、ライフタイムに関する悩みも避けられます。

impl ServerConfigBuilder {
    pub fn port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }
    pub fn use_tls(mut self, yes: bool) -> Self {
        self.use_tls = yes;
        self
    }
}

build()でデフォルトを適用する

最後のbuild()はbuilderを実際の型に変換します。ここで、まだNoneのものにunwrap_orを使ってデフォルト値を設定します。

impl ServerConfigBuilder {
    pub fn build(self) -> ServerConfig {
        ServerConfig {
            host: self.host,
            port: self.port.unwrap_or(8080),
            max_connections: self.max_connections.unwrap_or(128),
            use_tls: self.use_tls,
        }
    }
}

流れるような構築

これで構築処理を上から下へ読めるようになります。必須データはbuilder()に渡し、省略可能な設定はそれぞれ意味の分かる名前の呼び出しで指定します。

省略したフィールドには、暗黙的にデフォルト値が使われます。

fn main() {
    let cfg = ServerConfig::builder("localhost")
        .port(9000)
        .use_tls(true)
        .build();
    println!("{}:{} tls={}", cfg.host, cfg.port, cfg.use_tls);
}

実行可能な完全な例

ここでは、このパターン全体を実行できる1つのプログラムにまとめています。省略したフィールドがbuild()内でデフォルト値に戻ることを確認できます。

struct Config { name: String, retries: u32 }
struct Builder { name: String, retries: Option<u32> }
impl Config {
    fn builder(name: &str) -> Builder {
        Builder { name: name.to_string(), retries: None }
    }
}
impl Builder {
    fn retries(mut self, n: u32) -> Self { self.retries = Some(n); self }
    fn build(self) -> Config {
        Config { name: self.name, retries: self.retries.unwrap_or(3) }
    }
}
fn main() {
    let c = Config::builder("job").build();
    println!("{} retries={}", c.name, c.retries);
}

Resultを返す失敗可能なbuild

たとえばポート番号が0の場合など、設定が無効になることがあります。build()がResultを返すようにすると、検証の失敗をパニックではなく回復可能なエラーとして扱えます。

impl ServerConfigBuilder {
    pub fn try_build(self) -> Result<ServerConfig, String> {
        let port = self.port.unwrap_or(8080);
        if port == 0 {
            return Err("port must be non-zero".into());
        }
        Ok(ServerConfig { host: self.host, port,
            max_connections: self.max_connections.unwrap_or(128),
            use_tls: self.use_tls })
    }
}

derive_builderクレート

builderを手作業で記述すると、同じようなコードが繰り返し必要になります。derive_builderクレートは、アノテーションからbuilder全体を生成します。

フィールドにデフォルト値を指定するアノテーションを付けると、setterと失敗可能なbuild()を備えたFooBuilderが自動生成されます。

use derive_builder::Builder;

#[derive(Builder)]
struct Channel {
    #[builder(default = "8080")]
    port: u16,
    name: String,
}

所有型Builderと可変参照Builder

2つのスタイルがあります。所有型のスタイルはselfを消費し、自然にチェーンできます。&mut selfのスタイルは&mut Selfを返し、再束縛せずに複数の文に分けて構築できます。

所有型は1回限りの構築でより一般的で、可変型はループ内などで条件付き設定を行う場合に適しています。

impl ServerConfigBuilder {
    pub fn port_ref(&mut self, port: u16) -> &mut Self {
        self.port = Some(port);
        self
    }
}

クイックチェック

所有するselfを使うbuilderスタイルについての理解度を確認しましょう。

まとめ

builderパターンは、Rustに省略可能な引数がないという制限を補います。builder型が構築途中の状態を保持し、setterはselfを消費して返すことでチェーンを可能にし、build()がデフォルト値を適用して最終的な値を生成します。

検証にはbuild()からResultを返し、定型コードを省きたい場合はderive_builderを利用してください。

よくある質問

「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は初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「Builderパターン」レッスンにはどのくらい時間がかかりますか?

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

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

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

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

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