生成時に不変条件を強制する
newtype パターンを活用すると、不変条件 を強制できます。
// Copyright 2025 Google LLC // SPDX-License-Identifier: Apache-2.0 pub struct Username(String); impl Username { pub fn new(username: String) -> Result<Self, InvalidUsername> { if username.is_empty() { return Err(InvalidUsername::CannotBeEmpty) } if username.len() > 32 { return Err(InvalidUsername::TooLong { len: username.len() }) } Ok(Self(username)) } pub fn as_str(&self) -> &str { &self.0 } } pub enum InvalidUsername { CannotBeEmpty, TooLong { len: usize }, }
-
newtype パターンは、Rust のモジュールシステムおよび可視性システムと組み合わせることで、 ある型のインスタンスが一連の 不変条件を満たすことを 保証 するために使用できます。
上の例では、
Username構造体の内部に格納されている生のStringには、 他のモジュールやクレートから直接アクセスできません。これは、それがpubやpub(in ...)としてマークされていないためです。Username型の利用者は、 インスタンスを作成するためにnewメソッドを使うことを強制されます。その結果、newがバリデーションを実行し、Usernameのすべてのインスタンスがそれらのチェックを満たすことを 保証します。 -
as_strメソッドにより、利用者は生の文字列表現にアクセスできます (たとえば、それをデータベースに保存するために)。ただし、戻り値の型である&strは読み取り専用のアクセスに制限するため、利用者は基になる値を 変更できません。 -
型レベルの不変条件には、副次的な利点もあります。
入力は境界で一度だけ検証され、その後のプログラムの残りの部分は 不変条件が維持されていることを前提にできます。これにより、プログラム全体にわたる 冗長な検証や「防御的プログラミング」のチェックを避けられ、ノイズを減らし パフォーマンスを向上できます。