Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Обеспечивайте инварианты при конструировании

Паттерн 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 можно использовать, чтобы гарантировать, что экземпляры заданного типа удовлетворяют набору инвариантов.

    В примере выше «сырой» String, хранящийся внутри структуры Username, нельзя получить напрямую из других модулей или крейтов, поскольку он не помечен как pub или pub(in ...). Потребители типа Username вынуждены использовать метод new, чтобы создавать экземпляры. В свою очередь new выполняет проверку, гарантируя, что все экземпляры Username удовлетворяют этим проверкам.

  • Метод as_str позволяет потребителям получить сырое строковое представление (например, чтобы сохранить его в базе данных). Однако потребители не могут изменить базовое значение, поскольку возвращаемый тип &str ограничивает их доступом только для чтения.

  • Инварианты на уровне типов дают вторичные преимущества.

    Входные данные проверяются один раз, на границе, а остальная часть программы может полагаться на выполнение инвариантов. Можно избежать избыточной проверки и проверок «оборонительного программирования» по всей программе, уменьшив шум и улучшив производительность.