Выберите форму данных для программы и разберитесь, когда значения с одинаковыми полями имеют разные типы. В примере вы создадите пользователя и измените его состояние, сохранив исходное значение.
Установите Топаз по уроку Первая программа. Если вы впервые работаете с записями, начните с урока Данные и управление.
Создайте и обновите пользователя
Сохраните код в файле records-nominal-data.tpz в своей рабочей папке.
type ОтображаемоеЗначение = int | string
newtype ИдентификаторПользователя = int
enum Состояние {
Активен,
Приостановлен(string),
}
record Пользователь {
идентификатор: ИдентификаторПользователя,
имя: string,
состояние: Состояние = Состояние.Активен,
}
function меткаСостояния(состояние: Состояние) -> string {
match состояние {
case Активен => "active"
case Приостановлен(причина) => причина
}
}
function показать(значение: ОтображаемоеЗначение) -> string {
match значение {
case число: int => "number {число}"
case текст: string => текст
}
}
let до: Пользователь = Пользователь {
идентификатор: ИдентификаторПользователя(7),
имя: "Ada",
}
let после: Пользователь = Пользователь {
...до,
состояние: Состояние.Приостановлен("review"),
}
let сводка = { имя: "Ada", возраст: 36 }
let старше = сводка{ возраст: 37 }
let готово = показать("ready")
print("{до.идентификатор.value()}:{до.имя}:{меткаСостояния(до.состояние)}")
print("{после.имя}:{меткаСостояния(после.состояние)}")
print("{сводка.возраст}:{старше.возраст}")
print("{показать(7)}:{готово}")В этой же папке проверьте файл и запустите его.
topaz check records-nominal-data.tpz
topaz run records-nominal-data.tpzПроверка завершается с кодом 0. Программа печатает следующий результат.
7:Ada:active
Ada:review
36:37
number 7:readyПрочитайте результат
Запись до получает состояние Активен по умолчанию. Запись после сохраняет идентификатор и имя, но получает состояние Приостановлен("review"). Обновление сохраняет исходную запись и не вычисляет значения по умолчанию повторно.
сводка объединяет два поля без именованного объявления записи. Обновление возраста создаёт запись старше, поэтому третья строка содержит 36:37.
ОтображаемоеЗначение служит псевдонимом объединения. Оно принимает int или string без дополнительной оболочки. Шаблоны типов в функции показать выбирают подходящий тип и связывают значение с именем.
Различайте поля и объявления
Совместимость структурной записи определяется именами и типами полей. Например, сводка имеет структуру { имя: string, возраст: int }.
Номинальные записи различаются по объявлениям. Пользователь и отдельно объявленный Клиент остаются разными типами даже при одинаковых полях. Структурная запись с такими полями тоже не подходит вместо Пользователь.
Объявление newtype ИдентификаторПользователя = int создаёт отдельный тип вокруг целого числа. Создайте значение через ИдентификаторПользователя(7) и прочитайте число методом .value(). Каждый вызов снимает один слой.
| Что нужно | Что выбрать | Пример |
|---|---|---|
| Значение, определяемое набором полей | Структурная запись | сводка |
| Отдельный тип с именованными полями | Номинальная record | Пользователь |
| Один из объявленных вариантов | enum | Состояние.Активен или Состояние.Приостановлен(причина) |
| Значение одного из существующих типов | Объединение A | B | ОтображаемоеЗначение |
| Отдельный тип вокруг одного базового значения | newtype | ИдентификаторПользователя |
Набор вариантов перечисления задан в его объявлении. Создавайте вариант через Состояние.Приостановлен("review"), а сопоставляйте через case Приостановлен(причина). Проверяющая программа требует обработать все варианты перечисления. Дополнительное условие ветви называют guard. Ветви с таким условием не учитываются при проверке полноты.
Создавайте и обновляйте поля
Топаз проверяет имена и типы полей. Явно заданные поля вычисляются слева направо. Затем значения по умолчанию для пропущенных полей вычисляются один раз в порядке объявления.
При обновлении номинальной записи поставьте один ...source в начало, затем укажите заменяемые поля. Источник должен соответствовать тому же объявлению записи и тем же аргументам типа. Топаз вычисляет источник первым. Затем вычисляет замены слева направо и записывает их в соответствующие поля.
Обновление копирует внешнюю запись. Изменяемые значения полей, например массивы, остаются общими для исходной записи и результата.
Если запись передаёт внешняя программа, проверки во время выполнения требуют точного набора объявленных полей. Они выполняются до вычисления замен.
| Ошибка во входной записи | Ошибка выполнения |
|---|---|
| Пропущенное или повторяющееся поле | GUARD_ARITY (TPZ5004) |
| Неизвестное поле | GUARD_NO_FIELD (TPZ5006) |
Сопоставляйте записи и исправляйте ошибки
Шаблон case Пользователь { имя } => проверяет объявление и читает имя. Неуказанные поля игнорируются. Укажите хотя бы одно поле и используйте имя записи без пространства имён.
В примере обновляйте структурную запись через сводка{ возраст: 37 }, а именованную через Пользователь { ...до, состояние: Состояние.Приостановлен("review") }. Форма JavaScript { ...сводка } в Топазе недопустима.
Поместите методы в отдельный блок impl Пользователь на уровне модуля. Его self неизменяем и передаётся по значению. Тело записи не предоставляет неявные this, &self или &mut self.
Другие правила данных
- Объявляйте записи, перечисления и новые типы на уровне модуля.
- Обобщённые номинальные типы инвариантны, поэтому изменение аргумента типа даёт другой тип. Нужный при создании ожидаемый тип описан в разделе Типы.
- Равенство, упорядочение и использование в качестве ключа коллекции учитывают объявление и содержимое. Вложенные типы тоже должны поддерживать операцию.
- Рефлексия, наследование и типы записей с неопределённым набором дополнительных полей не поддерживаются. Номинальные шаблоны используют объявленные поля.
- Создание записей и новых типов через пространство имён, выбор реализации протокола во время выполнения и получатели в стиле владения Rust не поддерживаются.
Продолжите в разделе Шаблоны и управление потоком, чтобы выбирать ветви и извлекать значения.