Lekcje modułu (2/2)
Dopasowanie wzorców, Option i Result
Rust posiada niezwykle potężny mechanizm sterowania przepływem zwany dopasowywaniem wzorców (pattern matching). Pozwala on na porównanie wartości z serią wzorców i wykonanie kodu na podstawie dopasowanego wzorca.
Instrukcja match
Instrukcja match w języku Rust przypomina switch znany z innych języków, ale jest znacznie potężniejsza i bezpieczniejsza. Kompilator gwarantuje, że dopasowanie jest wyczerpujące (esaustivo), co oznacza, że wszystkie możliwe przypadki muszą zostać obsłużone:
enum Direction {
North,
South,
East,
West,
}
let dir = Direction::East;
match dir {
Direction::North => println!("Andiamo a Nord"),
Direction::South => println!("Andiamo a Sud"),
Direction::East => println!("Andiamo a Est"),
Direction::West => println!("Andiamo a Ovest"),
}
Obsługa braku wartości: Option
Rust nie posiada pojęcia wartości pustej (null lub nil). Zamiast tego biblioteka standardowa definiuje typ wyliczeniowy (enum) o nazwie Option<T>, który może zawierać:
Some(T): zawiera wartość typuT.None: oznacza brak wartości.
let x: Option<i32> = Some(5);
let y: Option<i32> = None;
match x {
Some(val) => println!("C'e un valore: {}", val),
None => println!("Nessun valore trovato"),
}
Obsługa błędów odwracalnych: Result
W przypadku operacji, które mogą się nie powieść (np. odczyt pliku lub konwersja tekstu na liczbę), Rust używa typu wyliczeniowego Result<T, E>, który posiada dwa warianty:
Ok(T): operacja zakończyła się sukcesem i zawiera pomyślnie uzyskaną wartośćT.Err(E): operacja nie powiodła się i zawiera błądE.
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> {
if denominator == 0.0 {
Err(String::from("Divisione per zero!"))
} else {
Ok(numerator / denominator)
}
}
Dzięki kompilatorowi Rusta jesteś zobowiązany do obsłużenia przypadku Err zanim uzyskasz dostęp do wartości wewnątrz Ok, co zapobiega wielu błędom w czasie wykonywania programu.
Wzorzec wieloznaczny _ (Wildcard)
Podczas pracy z typami danych posiadającymi bardzo dużą liczbę wariantów (jak liczby całkowite), wymienienie wszystkich przypadków w instrukcji match jest niemożliwe. Rust pozwala na użycie znaku wieloznacznego _ w celu przechwycenia wszystkich pozostałych, jawnie niezdefiniowanych przypadków:
let some_u8 = 3u8;
match some_u8 {
1 => println!("Uno"),
2 => println!("Due"),
_ => println!("Qualcos'altro"), // Gestisce tutti gli altri valori
}
Spróbuj sam
Zadeklaruj zmienną maybe_value typu Option<i32> zawierającą Some(42). Użyj instrukcji match na maybe_value: jeśli zawiera Some(v) wypisz 'value is: v' (za pomocą println!), jeśli zawiera None wypisz 'no value'.
Pokaż wskazówkę
Zapisz blok `match maybe_value { Some(v) => println!('value is: {}', v), None => println!('no value') }`.
Rozwiązanie dostępne po 3 próbach
Uzupełnij funkcję check_age tak, aby zwracała Ok('Adult'), jeśli age jest większe lub równe 18, lub Err('Minor') w przeciwnym razie.
Pokaż wskazówkę
Użyj `if age >= 18`do podjęcia decyzji, czy zwrócić`Ok('Adult')`czy`Err('Minor')`.
Rozwiązanie dostępne po 3 próbach
Zadeklaruj enum o nazwie Status zawierający warianty Active i Inactive. W funkcji main zadeklaruj zmienną current_status o wartości Status::Active. Na koniec użyj instrukcji match na current_status, aby wypisać 'active', jeśli status to Active, lub 'inactive', jeśli status to Inactive.
Pokaż wskazówkę
Zdefiniuj enum przed funkcją main za pomocą `enum Status { Active, Inactive }`. W main użyj `let current_status = Status::Active;`i wykonaj dopasowanie`match` na wariantach.
Rozwiązanie dostępne po 3 próbach