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

За пределами простого typestate

Как управлять всё более сложными потоками конфигурации с множеством возможных состояний и переходов, по-прежнему предотвращая несовместимые операции?

// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

struct Serializer {/* [...] */}
struct SerializeStruct {/* [...] */}
struct SerializeStructProperty {/* [...] */}
struct SerializeList {/* [...] */}

impl Serializer {
    // TODO, реализовать:
    //
    // fn serialize_struct(self, name: &str) -> SerializeStruct
    // fn finish(self) -> String
}

impl SerializeStruct {
    // TODO, реализовать:
    //
    // fn serialize_property(mut self, name: &str) -> SerializeStructProperty

    // TODO,
    // Как завершить эту структуру? Это зависит от того, где она находится:
    // - На корневом уровне: вернуть `Serializer`
    // - Как свойство внутри другой структуры: вернуть `SerializeStruct`
    // - Как значение внутри списка: вернуть `SerializeList`
    //
    // fn finish(self) -> ???
}

impl SerializeStructProperty {
    // TODO, реализовать:
    //
    // fn serialize_string(self, value: &str) -> SerializeStruct
    // fn serialize_struct(self, name: &str) -> SerializeStruct
    // fn serialize_list(self) -> SerializeList
    // fn finish(self) -> SerializeStruct
}

impl SerializeList {
    // TODO, реализовать:
    //
    // fn serialize_string(mut self, value: &str) -> Self
    // fn serialize_struct(mut self, value: &str) -> SerializeStruct
    // fn serialize_list(mut self) -> SerializeList

    // TODO:
    // Как и в случае с `SerializeStruct::finish`, тип возвращаемого значения зависит от вложенности.
    //
    // fn finish(mut self) -> ???
}

Схема допустимых переходов:

structureserializerpropertylistString
  • Развивая предыдущий сериализатор, мы теперь хотим поддержать вложенные структуры и списки.

  • Однако это приводит одновременно к дублированию и структурной сложности.

  • Что ещё важнее, мы упираемся в ограничение системы типов: мы не можем аккуратно выразить, что должен возвращать finish(), не дублируя варианты для каждого контекста вложенности (например, корень, структура, список).

  • По схеме допустимых переходов можно заметить:

    • Переходы рекурсивны
    • Тип возвращаемого значения зависит от того, где появляется вложенная структура или список
    • Каждому контексту нужен путь возврата к своему родителю
  • Если использовать только конкретные типы, это становится неуправляемым. Наш текущий подход приводит к взрыву количества типов и ручной обвязке.

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