生成時に不変条件を強制する

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 には、 他のモジュールやクレートから直接アクセスできません。これは、それが pubpub(in ...) としてマークされていないためです。Username 型の利用者は、 インスタンスを作成するために new メソッドを使うことを強制されます。その結果、 new がバリデーションを実行し、Username のすべてのインスタンスがそれらのチェックを満たすことを 保証します。

  • as_str メソッドにより、利用者は生の文字列表現にアクセスできます (たとえば、それをデータベースに保存するために)。ただし、戻り値の型である &str は読み取り専用のアクセスに制限するため、利用者は基になる値を 変更できません。

  • 型レベルの不変条件には、副次的な利点もあります。

    入力は境界で一度だけ検証され、その後のプログラムの残りの部分は 不変条件が維持されていることを前提にできます。これにより、プログラム全体にわたる 冗長な検証や「防御的プログラミング」のチェックを避けられ、ノイズを減らし パフォーマンスを向上できます。