Combinadores: map

match es un método válido para manejar Options. Sin embargo, eventualmente puedes encontrar tedioso el uso intensivo, especialmente con operaciones que solo son válidas con una entrada. En estos casos, combinadores se pueden utilizar para gestionar el flujo de control de forma modular.

Option tiene un método incorporado llamado map(), un combinador para el mapeo simple de Some -> Some y None -> None. Se pueden encadenar múltiples llamadas map() juntas para una mayor flexibilidad.

En el siguiente ejemplo, procesar() reemplaza todas las funciones anteriores mientras se mantiene compacto.

#![allow(dead_code)]

#[derive(Debug)] enum Comida { Manzana, Zanahoria, Papa }

#[derive(Debug)] struct Pelada(Comida);
#[derive(Debug)] struct Cortada(Comida);
#[derive(Debug)] struct Cocida(Comida);

// Pela la comida. Si no hay ninguna, devuelve `None`.
// De lo contrario, devuelvw la comida pelada.
fn pelar(comida: Option<Comida>) -> Option<Pelada> {
    match comida {
        Some(comida) => Some(Pelada(comida)),
        None         => None,
    }
}

// Corta la comida. Si no hay ninguna, devuelva `None`.
// De lo contrario, devuelva la comida picada.
fn cortar(pelada: Option<Pelada>) -> Option<Cortada> {
    match pelada {
        Some(Pelada(comida)) => Some(Cortada(comida)),
        None                 => None,
    }
}

// Cocina la comida. Aquí, mostramos `map()` en lugar de `match` para el manejo de casos.
fn cocinar(cortada: Option<Cortada>) -> Option<Cocida> {
    cortada.map(|Cortada(comida)| Cocida(comida))
}

// Una función para pelar, cortar y cocinar la comida, todo en secuencia.
// Encadenamos múltiples usos de `map()` para simplificar el código.
fn procesar(comida: Option<Comida>) -> Option<Cocida> {
    comida.map(|f| Pelada(f))
        .map(|Pelada(f)| Cortada(f))
        .map(|Cortada(f)| Cocida(f))
}

// ¡Comprueba si hay comida o no antes de intentar comerla!
fn comer(comida: Option<Cocida>) {
    match comida {
        Some(comida) => println!("Mmm. Amo {:?}", comida),
        None         => println!("¡Oh, no! No era comestible."),
    }
}

fn main() {
    let manzana = Some(Comida::Manzana);
    let zanahoria = Some(Comida::Zanahoria);
    let papa = None;

    let manzana_cocida = cocinar(cortar(pelar(manzana)));
    let zanahoria_cocida = cocinar(cortar(pelar(zanahoria)));
    // Probemos ahora con el `procesar()` de apariencia más simple.
    let papa_cocida = procesar(papa);

    comer(manzana_cocida);
    comer(zanahoria_cocida);
    comer(papa_cocida);
}

Ve también

clausuras, Option, Option::map()