Menu

Enum w Zero: proste wyliczenia bez danych

Jak działa enum w Zero: deklarowanie stałego zbioru nazwanych wariantów, porównywanie wartości i granica między enum (zwykłe etykiety) a choice (unie z etykietami).

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Deklarowanie enum

enum deklaruje typ, którego wartości tworzą stały zbiór nazwanych wariantów:

enum Status {
    ready,
    failed,
}

Teraz Status to typ z dokładnie dwiema wartościami: Status.ready i Status.failed. Nic innego nie może być typu Status.

Gramatyka jest celowo malutka:

  • enum Name { otwiera deklarację.
  • Każda linia zawiera nazwę jednego wariantu, oddzieloną przecinkiem.
  • } zamyka deklarację.

Żadnych danych, wartości dyskryminatora ani metod pochodnych: dzięki temu enum pozostaje w Zero "małym typem sumowym".

Używanie enum

Wariant nazywasz, poprzedzając go nazwą typu enum:

let state: Status = Status.ready

Adnotacja : Status jest opcjonalna, gdy prawa strona ustala typ; w większości przypadków możesz napisać:

let state = Status.ready

a kompilator wywnioskuje typ Status.

Porównywanie wartości enum

Dwie wartości enum są równe, gdy są tym samym wariantem:

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

To najprostszy sposób rozgałęzienia na enum. Do wyczerpującej analizy, czyli jawnej obsługi każdego wariantu, sięgnij po match:

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

Przewaga match nad if/else if wychodzi na jaw, gdy później dodasz trzeci wariant. Kompilator wskaże każde match, w którym brakuje nowego przypadku; łańcuch if/else po cichu przejdzie do domyślnej gałęzi.

Artykuł Choice i match omawia match dokładniej. Działa ono zarówno dla enum, jak i dla choice.

Pełny przykład

Oficjalna próbka Zero zestawia enum i choice w jednym pliku:

Status nic w tym fragmencie nie robi: jest tu, żeby pokazać kontrast. Wariant choice przy dopasowaniu wiąże dane (value, message); wariant enum nie wiąże niczego, bo nie ma czego wiązać.

Enum czy choice: szybkie drzewo decyzyjne

Krótka zasada:

  • Warianty to tylko etykiety → enum.
  • Warianty muszą nieść dane → choice.

Jeśli modelujesz etapy cyklu życia i w końcu musisz dołączyć komunikat o błędzie do stanu "failed", zmień typ z enum na choice. Każdy wariant dostaje typ danych, a gałęzie match w dalszym kodzie zyskują wiązanie dla tych danych. To refaktoryzacja, przez którą prowadzi cię kompilator.

Konkretnie:

// Przed: enum, bez danych
enum Status {
    ready,
    failed,
}

// Po: choice z danymi przy każdym wariancie
choice Status {
    ready: Void,
    failed: String,
}

Warianty z danymi typu Void to w formie choice zwykłe etykiety. Możesz używać enum i choice dla tych samych logicznych stanów; wybierz enum, gdy naprawdę nie potrzebujesz dołączonych danych.

Zastosowania

Kilka codziennych przykładów, w których enum jest właściwą odpowiedzią:

  • Cykl życia bez metadanych. Loading, Ready, Empty: czyste stany, bez danych.
  • Tryby. Read, Write, Append jako tryb otwarcia pliku.
  • Kierunek. North, South, East, West.
  • Poziom logowania. Trace, Debug, Info, Warn, Error. (Później możesz chcieć dodać komunikat i wtedy przejdziesz na choice.)
  • Dzień tygodnia. Kanoniczny przykład.

Zawsze gdy kusi cię magiczna stała liczbowa (0 = oczekuje, 1 = aktywne, 2 = gotowe), enum prawie na pewno będzie czytelniejszy.

Uwagi o stylu

  • Nazwy wariantów małymi literami pasują do stylu identyfikatorów w całym języku Zero.
  • Przecinek po ostatnim wariancie jest w porządku (i zalecany ze względu na czytelne diffy: dodanie nowego wariantu nie zmienia poprzedniej linii).
  • Utrzymuj listy enum krótkie. Jeśli masz kilkanaście wariantów, a wiele z nich potrzebuje danych, być może patrzysz na choice albo potrzebę przeprojektowania, a nie na większy enum.

Dalej: choice i match

Naturalnym następnym krokiem jest bogatszy kuzyn: choice i match, czyli typ unii z etykietami w Zero i towarzysząca mu konstrukcja dopasowania wzorców.

Najczęściej zadawane pytania

Czym jest enum w Zero?

enum deklaruje typ, którego wartości należą do stałego zbioru nazwanych wariantów: etykiet bez żadnych dodatkowych danych. Przykład: enum Status { ready, failed }. Wartość typu Status to dokładnie jedno z Status.ready lub Status.failed, a kompilator tego pilnuje.

Czym enum różni się od choice?

Warianty enum to zwykłe etykiety: nie niosą danych. choice to unia z etykietami: każdy wariant ma powiązany typ danych, na przykład choice Result { ok: i32, err: String }. Używaj enum, gdy wystarczy rozróżniać przypadki po nazwie, a choice, gdy każdy przypadek niesie dodatkowe informacje.

Jak sprawdzić, jaki wariant enum ma wartość?

Porównaj wartość z wariantem: if status == Status.ready { ... }. Do wyczerpującego rozgałęzienia po wszystkich wariantach użyj match: kompilator ostrzeże, jeśli pominiesz wariant, i to główny powód, by wybierać match zamiast łańcuchów if/else if, gdy wartość jest typu sumowego.

Czy warianty enum mogą mieć powiązane wartości w Zero?

Nie: do tego służy choice. enum to celowo minimalny typ sumowy, w którym każdy wariant jest tylko etykietą. Jeśli potrzebujesz dołączyć i32 albo String do któregoś wariantu, enum już nie wystarcza i potrzebujesz choice.

Kiedy używać enum w Zero?

Używaj enum, gdy wartość musi być dokładnie jednym z małego, nazwanego zbioru stanów, a te stany nie niosą dodatkowych danych. Przykłady: dzień tygodnia, kolor sygnalizacji świetlnej, etap cyklu życia bez metadanych, poziom logowania. Jeśli chcesz dołączyć dane do któregoś wariantu, przejdź na choice.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ