RustにはC++やJavaのような従来のコンストラクタはありませんが、タイプのオブジェクトを作成するためには通常、関連関数(しばしばnewという名前)やいわゆるファクトリメソッドが使用されます。これは、言語の歴史に起因しており、安全性と初期化の明示性に特に注意が払われているためです:各フィールドの正しい初期化において、明示的に書かれた関数だけが責任を持ちます。
問題の歴史
最初はRustでは構造体の初期化がすべてのフィールドを直接指定することを許していました(いわゆる「struct literal」構文)。しかし、不変性を確保し、詳細を隠し、追加の検証を実装するため、ファクトリメソッド(impl SomeStruct { fn new(...) -> Self { ... } })やパターンを通じた一般化(ビルダーパターン)が実践されています。
問題
主な課題は、部分的に初期化されたオブジェクトを許可せず、不正な状態の構造体の使用を不可能にすることです。これは、リソースに関連する複雑な構造体(ファイル、ソケットなど)に特に重要であり、すべてのフィールドを手動で初期化することはエラーを引き起こす可能性があります。
解決策
Rustでは、完全に初期化されたオブジェクトを返し、必要に応じて検証を行い、インスタンス化の詳細を隠すファクトリメソッドを作成することが推奨されています。
コードの例:
struct User { username: String, age: u8, } impl User { pub fn new(username: String, age: u8) -> Option<Self> { if age >= 18 { Some(Self { username, age }) } else { None } } } fn main() { let user = User::new("Alice".to_string(), 20); // user: Option<User>, エラーを安全に処理する }
主な特徴:
fn new)。モジュールの外で直接インスタンスを作成できないように、構造体のプライベートフィールドを作成できますか?
はい、すべてのフィールドをプライベートにし、公開ファクトリメソッドだけを提供すれば、その構造体はモジュールの外で直接初期化できません。
ファクトリメソッドは常にnewと名付ける必要がありますか?
いいえ、それは慣例ですが、必須ではありません。さまざまな初期化戦略に応じて、「with_capacity」、「from_config」、「from_env」などの名前が使用されます。
プライベートコンストラクタはあり得ますか?
はい、関連関数がfn new(...) -> Selfと宣言され、pub修飾子が付いていない場合、この関数はこのモジュールの外で呼び出せません。これにより、シングルトン、エンフォースファクトリ、隠れた初期化などを実装できます。
ファクトリメソッドなしで公開フィールドを持つ構造体の直接使用:
struct Connection { fd: i32, timeout: u64, } let c = Connection { fd: -1, timeout: 10 }; // fd: -1はディスクリプタとして無効です!
利点:
欠点:
プライベートフィールドとファクトリメソッドの使用:
pub struct Connection { fd: i32, timeout: u64, } impl Connection { pub fn new(fd: i32, timeout: u64) -> Option<Self> { if fd >= 0 { Some(Self { fd, timeout }) } else { None } } }
利点:
欠点: