Enum (wyliczenia) w Rust zasadniczo różnią się od enum w C/C++: mogą przechowywać dane skojarzone i idealnie nadają się do modelowania stanu i błędów. Dzięki nim można budować typowo bezpieczne finite-state machines, różne rodzaje Option/Result oraz wdrażać wzorzec "sum types". Historycznie podobne konstrukcje były stosowane w językach funkcjonalnych do opisywania wariantów bytu z ściśle oddzielonymi wariantami.
Problem: osiągnięcie ekspresyjności (wyrażenie wszystkich wariantów stanu), gdzie każdy przypadek przetwarzania jest obowiązkowy i nie można przypadkowo pominąć gałęzi. Błędy projektowe są trudne do typizacji bez tak ekspresyjnej struktury.
Rozwiązanie: enum z danymi skojarzonymi i dopasowywanie wzorców dają kontrolę — każda gałąź jest sprawdzana przez kompilator, zapewniając wyczerpującość. Ponadto dla Result i Option już zaimplementowano wiele metod pomocniczych.
Przykład kodu:
enum NetworkState { Disconnected, Connecting(u32), // numer próby Connected(String), Error(String), } fn print_state(state: NetworkState) { match state { NetworkState::Disconnected => println!("Net: disconnected"), NetworkState::Connecting(count) => println!("Net: connecting (attempt {})", count), NetworkState::Connected(addr) => println!("Net: connected to {}", addr), NetworkState::Error(msg) => println!("Net error: {}", msg), } }
Kluczowe cechy:
Czy można częściowo przetwarzać gałęzie enum bez _?
Kompilator zabrania niezamkniętych przypadków dla non-exhaustive enum, ale jeśli użyje się _, to nieprzetworzone gałęzie będą "wchłaniane". Należy unikać _ w krytycznych gałęziach, aby przyszłe zmiany nie pozostały niezauważone.
W jakich przypadkach wartości skojarzone są przekazywane przez referencję, a w jakich kopiowane podczas dopasowywania wzorców?
Podczas dopasowywania wzorców dane skojarzone są domyślnie przenoszone (move). Jeśli potrzebny jest tylko podgląd, użyj referencji:
match &state { NetworkState::Connected(addr) => println!("by ref: {}", addr), _ => {} }
Czy można w jednej strukturze używać dwóch enum z nakładającymi się wariantami po nazwie?
Można, ale nazwy wariantów są używane z prefiksem enum. To eliminuje kolizje i czyni kod samodokumentującym (na przykład, Status::Ok vs NetworkState::Ok).
_ do ukrywania coraz nowych wariantów podczas rozszerzania enum_ =>) w krytycznych obsługach błędówW kodzie obsługa Result<T, E> zawsze ma catch-all przez _ =>, a nowe błędy (podczas rozszerzania enum) przechodzą obok — zdarzają się silent-loss błędy.
Zalety:
Wady:
Stosowane są exhaustiveness matching, każda gałąź Enum jest przetwarzana jawnie, lub panic w gałęzi, dla której brak jest stabilnego zachowania.
Zalety:
Wady: