TL;DR

Rust で構造体を作るためのパターン。公開フィールドだけを持つ初期化用の構造体に Default を実装し、必要なフィールドだけを上書きしてから init() で本来の構造体に変換する。

なぜ必要か

The Init Struct Pattern によると、pub fn new() に引数を並べる方法は、引数が増えると読みにくくなり、引数を足すと破壊的変更になる。Builder パターンはこの問題を避けられるが、フィールドごとにセッターを書くので冗長になる。Init パターンは、Builder パターンとほぼ同じ利点を、より少ないコードで得る。

仕組み

初期化用の構造体(YakShaverInit など)のフィールドをすべて公開にし、impl Default で既定値を与える。呼び出す側は構造体更新記法(..Default::default())で、指定したいフィールドだけを書く。複雑な初期化の処理は init() メソッドに置く。

例

let yak_shaver = YakShaverInit {
    clipper_size: 4,
    clipper_color: String::from("red"),
    ..Default::default()
}.init();

注意点

呼び出す側がすべてのフィールドを明示して書くと、後でフィールドを足したときにそのコードは壊れる。これは防げず、#[doc(hidden)] を付けたダミーのフィールドで抑止するしかない。初期化用の構造体には非公開のフィールドを置けない。作者は 2022 年 11 月の追記で、このパターンの代わりに derive_builder クレートを勧めている。

関連

出典