Эта страница описывает Топаз v5.17. Прежде чем считать её актуальной, сверьте поле version в /version.json со значением 5.17.
«У этих значений одинаковые поля» и «эти значения обозначают одно понятие предметной области» — разные обещания. Поэтому в Топазе есть несколько форм данных. Сначала решите, какое различие должна сохранять программа, а затем выберите самую простую подходящую форму.
Запустите пример
Сохраните код в файле 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
Программа напечатает:
7:Ada:active
Ada:review
36:37
number 7:readyВыберите форму данных
| Форма | Когда выбирать | Создание и чтение |
|---|---|---|
| Структурная запись | Локальному значению достаточно известного набора полей | { name: "Ada", age: 36 }, затем value.field |
Номинальная record | Важны имя предметной области и принадлежность объявлению | Пользователь { ... }; поля, значения по умолчанию, именное обновление и шаблоны |
enum | Значение является ровно одним вариантом из закрытого набора | Состояние.Активен или Состояние.Приостановлен(причина); выбор через match |
Объединение A | B | Подходит значение одного из уже существующих типов | Сужение с помощью литерального, конструкторного или типового шаблона |
newtype | Базовому значению нужна отдельная предметная идентичность | ИдентификаторПользователя(7), затем снятие одного слоя методом .value() |
Пользователь — номинальная запись. Если поле состояние пропущено, его значение по умолчанию вычисляется один раз, поэтому до получает состояние Активен. Форма Пользователь { ...до, состояние: ... } создаёт новую внешнюю запись, сохраняет остальные поля, не меняет до и не запускает значения по умолчанию повторно.
Состояние — закрытое перечисление. При создании используется пространство имён (Состояние.Приостановлен), а в шаблоне — короткое имя варианта (case Приостановлен(причина)). Благодаря этому проверяющий может убедиться, что меткаСостояния обрабатывает все варианты.
ОтображаемоеЗначение — псевдоним объединения, а не новая оболочка времени выполнения. Типовые шаблоны в показать сужают существующее значение до int или string и связывают его с именем. ИдентификаторПользователя, напротив, остаётся отдельной номинальной оболочкой, хотя его базовый тип — int.
сводка — структурная запись: её совместимость определяется формой полей. Она обновляется как сводка{ возраст: 37 }. Это другая операция, не номинальное обновление Пользователь.
Создание, обновление и разбор
Поля номинальной записи проверяются по имени и типу. Явно заданные поля вычисляются слева направо, после них пропущенные значения по умолчанию вычисляются в порядке объявления. Номинальное обновление допускает один начальный ...source, за которым идут заменяемые поля. Копируется только внешняя запись, поэтому изменяемые значения внутри полей остаются общими.
Шаблон case Пользователь { имя } => сначала проверяет, что значение создано объявлением Пользователь, а затем читает выбранные поля. Неуказанные поля игнорируются, но хотя бы одно поле назвать необходимо. Структурная запись с такими же полями всё равно не становится Пользователь.
Частая ошибка
Форма JavaScript { ...value } не является обновлением структурной записи в Топазе. Для структурной записи используйте value{ field: replacement }, а для номинальной — Name { ...value, field: replacement }.
Не помещайте в тело записи синтаксис получателя из Rust. Неявных this, &self и &mut self нет. Если нужен метод-получатель, ограниченная форма Топаза записывается в отдельном блоке impl Name верхнего уровня и принимает обычный неизменяемый self по значению.
Точные границы
- Записи, перечисления и новые типы объявляются на верхнем уровне модуля и имеют номинальную идентичность.
- Параметры обобщённых номинальных типов инвариантны; при создании им нужен ожидаемый тип или другой разрешённый контекст вывода.
- Сравнение, порядок и ключи коллекций для номинальных значений сохраняют не только содержимое, но и принадлежность объявлению.
- Шаблоны перечислений используют короткие имена вариантов; полнота проверяется по ветвям без дополнительных условий.
- В начале шаблона номинальной записи стоит простое имя без пространства имён, а пустой номинальный шаблон недопустим.
- Конструктор
newtypeпринимает ровно одно базовое значение, а.value()снимает ровно один слой.
Отражение, наследование, полиморфизм строк записей, открытые номинальные шаблоны, создание через пространство имён, динамическая диспетчеризация протоколов и получатели в стиле владения Rust в текущий язык не входят.
Начните с урока Данные и управление. Псевдонимы и объединения описаны в разделе Типы, а полный разбор значений и связанные имена — в разделе Шаблоны и управление потоком.