Szukaj…


Wprowadzenie

Typ Option<T> jest odpowiednikiem typów zerowalnych dla Rust'a, bez wszystkich problemów z tym związanych. Większość C-jak języki pozwalają jakakolwiek zmienna będzie null , jeśli nie ma obecnych danych, ale Option Typ jest inspirowana języków funkcyjnych, które sprzyjają „składniki ewentualne” (np Haskell'a Maybe monada). Użycie typów Option pozwoli ci wyrazić pogląd, że dane mogą tam być lub nie (ponieważ Rust nie ma typów zerowalnych).

Tworzenie wartości opcji i dopasowania wzorca

// The Option type can either contain Some value or None.
fn find(value: i32, slice: &[i32]) -> Option<usize> {
    for (index, &element) in slice.iter().enumerate() {
        if element == value {
            // Return a value (wrapped in Some).
            return Some(index);
        }
    }
    // Return no value.
    None
}

fn main() {
    let array = [1, 2, 3, 4, 5];
    // Pattern match against the Option value.
    if let Some(index) = find(2, &array) {
        // Here, there is a value.
        println!("The element 2 is at index {}.", index);
    }

    // Check if the result is None (no value).
    if let None = find(12, &array) {
        // Here, there is no value.
        println!("The element 12 is not in the array.");
    }

    // You can also use `is_some` and `is_none` helpers
    if find(12, &array).is_none() {
        println!("The element 12 is not in the array.");
    }
}

Niszczenie opcji

fn main() {
    let maybe_cake = Some("Chocolate cake");
    let not_cake = None;

    // The unwrap method retrieves the value from the Option
    // and panics if the value is None
    println!("{}", maybe_cake.unwrap());

    // The expect method works much like the unwrap method,
    // but panics with a custom, user provided message.
    println!("{}", not_cake.expect("The cake is a lie."));

    // The unwrap_or method can be used to provide a default value in case
    // the value contained within the option is None. This example would
    // print "Cheesecake".
    println!("{}", not_cake.unwrap_or("Cheesecake"));

    // The unwrap_or_else method works like the unwrap_or method,
    // but allows us to provide a function which will return the
    // fallback value. This example would print "Pumpkin Cake".
    println!("{}", not_cake.unwrap_or_else(|| { "Pumpkin Cake" }));

    // A match statement can be used to safely handle the possibility of none.
    match maybe_cake {
        Some(cake) => println!("{} was consumed.", cake),
        None       => println!("There was no cake.")
    }

    // The if let statement can also be used to destructure an Option.
    if let Some(cake) = maybe_cake {
        println!("{} was consumed.", cake);
    }
}

Rozpakowywanie odwołania do Opcji będącej jej zawartością

Odniesienia do opcji &Option<T> nie można rozpakować, jeśli typu T nie można skopiować. Rozwiązaniem jest zmiana opcji na &Option<&T> za pomocą as_ref() .

Rdza zabrania przeniesienia własności przedmiotów podczas pożyczania obiektów. Kiedy sama opcja jest pożyczana ( &Option<T> ), jej zawartość jest również - pośrednio - pożyczana.

#[derive(Debug)]
struct Foo;
 
fn main() {
    let wrapped = Some(Foo);
    let wrapped_ref = &wrapped;
    
    println!("{:?}", wrapped_ref.unwrap()); // Error!
}

nie może się wydostać z pożyczonej treści [- wyjaśnić E0507]

Można jednak utworzyć odwołanie do zawartości Option<T> . Opcja as_ref() zwraca opcję dla &T , którą można rozpakować bez przeniesienia własności:

println!("{:?}", wrapped_ref.as_ref().unwrap());

Używanie opcji z mapą i and_then

Operacja map jest użytecznym narzędziem podczas pracy z tablicami i wektorami, ale może być również używana do funkcjonalnego radzenia sobie z wartościami Option .

fn main() {

    // We start with an Option value (Option<i32> in this case).
    let some_number = Some(9);

    // Let's do some consecutive calculations with our number.
    // The crucial point here is that we don't have to unwrap
    // the content of our Option type - instead, we're just
    // transforming its content. The result of the whole operation
    // will still be an Option<i32>. If the initial value of
    // 'some_number' was 'None' instead of 9, then the result
    //  would also be 'None'.
    let another_number = some_number
        .map(|n| n - 1) // => Some(8)
        .map(|n| n * n) // => Some(64)
        .and_then(|n| divide(n, 4)); // => Some(16)

    // In the last line above, we're doing a division using a helper
    // function (definition: see bottom).
    // 'and_then' is very similar to 'map', but allows us to pass a
    // function which returns an Option type itself. To ensure that we
    // don't end up with Option<Option<i32>>, 'and_then' flattens the
    // result (in other languages, 'and_then' is also known as 'flatmap').

    println!("{}", to_message(another_number));
    // => "16 is definitely a number!"

    // For the sake of completeness, let's check the result when
    // dividing by zero.
    let final_number = another_number
        .and_then(|n| divide(n, 0)); // => None

    println!("{}", to_message(final_number));
    // => "None!"
}

// Just a helper function for integer division. In case
// the divisor is zero, we'll get 'None' as result.
fn divide(number: i32, divisor: i32) -> Option<i32> {
    if divisor != 0  { Some(number/divisor) } else { None }
}

// Creates a message that tells us whether our
// Option<i32> contains a number or not. There are other
// ways to achieve the same result, but let's just use
// map again!
fn to_message(number: Option<i32>) -> String {
    number
        .map(|n| format!("{} is definitely a number!", n)) // => Some("...")
        .unwrap_or("None!".to_string()) // => "..."
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow