Menu

Zero Enums: простые перечисления без payload

Как работает enum в Zero: объявление фиксированного набора именованных вариантов, сравнение значений и разница между enum (просто метки) и choice (tagged union).

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Объявление enum

enum объявляет тип, значения которого — фиксированный набор именованных вариантов:

enum Status {
    ready,
    failed,
}

Теперь Status — это тип с ровно двумя значениями: Status.ready и Status.failed. Ничто другое не может быть Status.

Грамматика намеренно крошечная:

  • enum Name { открывает объявление.
  • Каждая строка перечисляет одно имя варианта, через запятую.
  • } закрывает.

Никаких payload, никаких значений-дискриминаторов, никаких производных методов — это и удерживает enum в роли «маленького sum-типа» в Zero.

Использование enum

Назовите вариант, квалифицировав его типом enum:

let state: Status = Status.ready

Аннотация : Status необязательна, как только правая часть закрепляет тип; в большинстве случаев можно писать:

let state = Status.ready

и компилятор сам выведет тип Status.

Сравнение значений enum

Два значения enum равны, когда это один и тот же вариант:

if state == Status.ready {
    check world.out.write("ready\n")
} else {
    check world.out.write("not ready\n")
}

Это самый простой способ ветвиться по enum. Для исчерпывающего разбора — обработать каждый вариант явно — берите match:

match state {
    .ready  => { check world.out.write("ready\n") }
    .failed => { check world.out.write("failed\n") }
}

Преимущество match перед if/else if проявится, когда позже вы добавите третий вариант. Компилятор покажет каждый match, где нет нового случая; цепочка if/else молча провалится в свой дефолтный бранч.

Choice и match разбирает match подробнее. Он работает и для enum, и для choice.

Разобранный пример

Официальный пример Zero ставит enum и choice рядом в одном файле:

Status в этом фрагменте ничего не делает — он стоит здесь ради контраста. Вариант choice привязывает payload (value, message) при матчинге; вариант enum не привязывает ничего, потому что привязывать нечего.

Enum vs Choice: короткое дерево решений

Короткое правило:

  • Варианты — просто меткиenum.
  • Варианты должны нести данныеchoice.

Если моделируете lifecycle-состояния и в итоге нужно прицепить сообщение об ошибке к состоянию «failed», переключайте тип с enum на choice. Каждый вариант получит тип payload, а нижестоящие arm match приобретут привязку для этого payload. Это рефакторинг, через который вас проведёт компилятор.

Конкретно:

// До — enum, без payload
enum Status {
    ready,
    failed,
}

// После — choice с payload на каждом варианте
choice Status {
    ready: Void,
    failed: String,
}

Варианты с payload Void в форме choice — это просто метки. Один и тот же логический набор состояний можно выразить и через enum, и через choice; выбирайте enum, когда вам действительно не нужно прикладывать данные.

Где это применяется

Несколько повседневных примеров, где enum — правильный ответ:

  • Lifecycle без метаданных. Loading, Ready, Empty — чистые состояния, без payload.
  • Режимы. Read, Write, Append для режима открытия файла.
  • Направление. North, South, East, West.
  • Уровень лога. Trace, Debug, Info, Warn, Error. (Позже, возможно, захочется приложить сообщение — тогда вы переключитесь на choice.)
  • День недели. Каноничный пример.

Везде, где иначе вы потянулись бы к магическим целочисленным константам (0 = pending, 1 = active, 2 = done), enum почти всегда понятнее.

Стиль

  • Имена вариантов в нижнем регистре — это в духе Zero и согласовано с остальными идентификаторами в языке.
  • Хвостовая запятая после последнего варианта — это нормально (и рекомендуется ради diff-дружелюбности — добавление нового варианта не трогает предыдущую строку).
  • Держите списки enum небольшими. Если у вас дюжина вариантов и многим из них хочется payload — возможно, перед вами choice — или повод пересмотреть дизайн, — а не более крупный enum.

Дальше: Choice и Match

Естественный следующий шаг — более богатый родственник: choice и match, tagged-union тип Zero и подходящая ему конструкция pattern matching.

Часто задаваемые вопросы

Что такое enum в Zero?

enum объявляет тип, значения которого — один из фиксированного набора именованных вариантов: метки без какого-либо payload. Пример: enum Status { ready, failed }. Значение типа Status — это ровно Status.ready или Status.failed, и компилятор это обеспечивает.

Чем enum отличается от choice?

Варианты enum — это просто метки, без данных. choice — это tagged union: у каждого варианта есть связанный тип payload, например choice Result { ok: i32, err: String }. Используйте enum, когда случаи различаются только именем; используйте choice, когда каждый случай несёт дополнительную информацию.

Как проверить, какой вариант enum в значении?

Сравните значение с вариантом: if status == Status.ready { ... }. Для исчерпывающего ветвления по всем вариантам берите match — компилятор предупредит, если вы пропустили вариант, и это главная причина предпочесть match цепочкам if/else if, когда значение — это sum-тип.

Могут ли варианты enum иметь связанные значения в Zero?

Нет — для этого есть choice. enum намеренно минимальный sum-тип: каждый вариант — просто метка. Если нужно прицепить i32 или String к одному из вариантов, вы переросли enum и хотите choice.

Когда стоит использовать enum в Zero?

Используйте enum, когда значение должно быть ровно одним из маленького именованного набора состояний, и эти состояния не несут дополнительных данных. Примеры: день недели, цвет светофора, lifecycle-состояние без метаданных, уровень логирования. Если хочется прицепить данные к одному из вариантов — переключайтесь на choice.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ