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,Appendjako 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 nachoice.) - 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
choicealbo potrzebę przeprojektowania, a nie na większyenum.
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.