Manejo encadenable de Option con and_then()

RustRustBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, se introduce el combinador and_then() como una alternativa encadenable a map() cuando se trabaja con Option<T>, ya que evita anidar Option<Option<T>> y simplifica la legibilidad del código.

Nota: Si el laboratorio no especifica un nombre de archivo, puede usar cualquier nombre de archivo que desee. Por ejemplo, puede usar main.rs, compilar y ejecutarlo con rustc main.rs &&./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/AdvancedTopicsGroup(["Advanced Topics"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/MemorySafetyandManagementGroup(["Memory Safety and Management"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/MemorySafetyandManagementGroup -.-> rust/lifetime_specifiers("Lifetime Specifiers") rust/AdvancedTopicsGroup -.-> rust/operator_overloading("Traits for Operator Overloading") subgraph Lab Skills rust/variable_declarations -.-> lab-99237{{"Manejo encadenable de Option con and_then()"}} rust/function_syntax -.-> lab-99237{{"Manejo encadenable de Option con and_then()"}} rust/expressions_statements -.-> lab-99237{{"Manejo encadenable de Option con and_then()"}} rust/lifetime_specifiers -.-> lab-99237{{"Manejo encadenable de Option con and_then()"}} rust/operator_overloading -.-> lab-99237{{"Manejo encadenable de Option con and_then()"}} end

Combinadores: and_then

map() se describió como una forma encadenable de simplificar las declaraciones match. Sin embargo, usar map() en una función que devuelve un Option<T> da como resultado el anidado Option<Option<T>>. Encadenar múltiples llamadas juntas puede luego resultar confuso. Aquí es donde entra en juego otro combinador llamado and_then(), conocido en algunos lenguajes como flatmap.

and_then() llama a su función de entrada con el valor envuelto y devuelve el resultado. Si el Option es None, entonces devuelve None en lugar de eso.

En el siguiente ejemplo, cookable_v3() devuelve un Option<Food>. Usar map() en lugar de and_then() habría dado un Option<Option<Food>>, que es un tipo no válido para eat().

#![allow(dead_code)]

#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }
#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }

// No tenemos los ingredientes para hacer Sushi.
fn have_ingredients(food: Food) -> Option<Food> {
    match food {
        Food::Sushi => None,
        _           => Some(food),
    }
}

// Tenemos la receta para todo excepto Cordon Bleu.
fn have_recipe(food: Food) -> Option<Food> {
    match food {
        Food::CordonBleu => None,
        _                => Some(food),
    }
}

// Para preparar un plato, necesitamos tanto la receta como los ingredientes.
// Podemos representar la lógica con una cadena de `match`:
fn cookable_v1(food: Food) -> Option<Food> {
    match have_recipe(food) {
        None       => None,
        Some(food) => have_ingredients(food),
    }
}

// Esto se puede escribir convenientemente de forma más compacta con `and_then()`:
fn cookable_v3(food: Food) -> Option<Food> {
    have_recipe(food).and_then(have_ingredients)
}

// De lo contrario, tendríamos que `aplanar()` un `Option<Option<Food>>`
// para obtener un `Option<Food>`:
fn cookable_v2(food: Food) -> Option<Food> {
    have_recipe(food).map(have_ingredients).flatten()
}

fn eat(food: Food, day: Day) {
    match cookable_v3(food) {
        Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food),
        None       => println!("Oh no. We don't get to eat on {:?}?", day),
    }
}

fn main() {
    let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi);

    eat(cordon_bleu, Day::Monday);
    eat(steak, Day::Tuesday);
    eat(sushi, Day::Wednesday);
}

Resumen

¡Felicidades! Has completado el laboratorio Combinadores: And_then. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.