返回选项时有些不需要吗?

vit*_*ral 3 rust

我正在做更多的组合器 "Rust by Example"的一部分,并决定自己去看看它用了多少努力map代替and_then.

在我的尝试中,我遇到了一些非常奇怪的东西(它甚至看起来像编译器错误).它看起来像我只需要返回一个Food类型时,返回类型应该是Option(Food)

在我看来,cookable下面的功能应该可以简化为一行:

have_ingredients(food).map(|f| can_cook(f))
Run Code Online (Sandbox Code Playgroud)

显然它也可以是以下内容:

have_ingredients(food).and_then(can_cook)
Run Code Online (Sandbox Code Playgroud)

虽然我没有看到这两个函数之间的根本区别,因为它们都返回了Option<U>.

这样做时我得到了一个奇怪的编译错误,所以我明确地将匹配分解如下 - 看起来编译器想要返回,Food即使返回类型是Some(Food).到底是怎么回事???

//! stack.rs
#[derive(Debug)]
enum Food {
    CordonBleu,
    Steak,
    Sushi,
}

#[derive(Debug)]
enum Day {
    Monday,
    Tuesday,
    Wednesday,
}

/// we don't have the ingredients for sushi
fn have_ingredients(food: Food) -> Option<Food> {
    match food {
        Food::Sushi => None,
        _ => Some(food),
    }
}

/// can cook anything but cordon blue
fn can_cook(food: Food) -> Option<Food> {
    match food {
        Food::CordonBlue => None,
        _ => Some(food),
    }
}

/// can be done using map
fn cookable(food: Food) -> Option<Food> {
    match have_ingredients(food).map(|f| can_cook(f)) {
        // Some(food) => food,  // Why is this correct???
        Some(food) => Some(food), // **error: mismatched types:
        None => None,
    }
}

fn eat(food: Food, day: Day) {
    match cookable(food) {
        Some(food) => println!("Yay! On {:?} we eat {:?}", day, food),
        None => println!("Oh no we didn'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);
}
Run Code Online (Sandbox Code Playgroud)

以下是上述程序的完整编译器错误:

 ch16_errors git:(master) ? rustc stack.rs
stack.rs:38:28: 38:32 error: mismatched types:
 expected `Food`,
    found `core::option::Option<Food>`
(expected enum `Food`,
    found enum `core::option::Option`) [E0308]
stack.rs:38         Some(food) => Some(food),
                                       ^~~~
stack.rs:38:28: 38:32 help: run `rustc --explain E0308` to see a detailed explanation
error: aborting due to previous error
Run Code Online (Sandbox Code Playgroud)

Vee*_*rac 6

have_ingredients(food).map(|f| can_cook(f))
Run Code Online (Sandbox Code Playgroud)

给出一个Option<Option<Food>>,而不是一个Option<Food>,因为map不会使价值变平.

考虑

Option<T>::map(Fn(T) -> U)
Run Code Online (Sandbox Code Playgroud)

这转变Option<T>Option<U>.因此,让T = Food,U = Option<Food>如在can_cook实例中

Option<Food>::map(Fn(Food) -> Option<Food>)
Run Code Online (Sandbox Code Playgroud)

这给了一个Option<Option<Food>>.

因此Some(food)在比赛中有food类型Option<Food>.

and_then 展平结果类型,这就是为什么不会出现这种情况.