如何在Option上实现一些便捷的方法(例如flat_map、flatten)?

13
如果Rust的Option提供类似于Option#flatten和Option#flat_map的一些附加便利方法,那就太好了。其中,flatten将把>>减少到Option,而flat_map则类似于map,但是使用返回Option的方法/闭包并将其展平。
flat_map非常简单:
fn opt_flat_map< T, U, F: FnOnce(T) -> Option<U> >(opt: Option<T>, f: F) -> Option<U> {
  match opt {
    Some(x) => f(x),
    None => None
  }
}

flatten 更为复杂,我不太清楚如何定义它。它可能看起来像这样:

fn opt_flatten<T, U>(opt: Option<T>) -> Option<U> {
  match opt {
      Some( Some(x) ) => flatten_option( Some(x) ),
      _ => opt
  }
}

但是这肯定行不通。你有什么想法吗?
此外,我该如何在Option枚举上实现这些方法,以便我可以在Option实例上本地使用它们?我知道我需要在impl OptionExts for Option<T>的某个地方添加类型签名,但我不知道该怎么做...
希望这样讲清楚了,对我的术语不够精确表示歉意 - 我是 Rust 的新手。
2个回答

23

这些可能已经存在,只是名称与您期望的不同。请查看Option文档

通常情况下,您会将flat_map称为and_then

let x = Some(1);
let y = x.and_then(|v| Some(v + 1));

更好的实现你想要的方式是声明一个带有你想要的方法的特质,然后为Option实现它:

trait MyThings {
    fn more_optional(self) -> Option<Self>;
}

impl<T> MyThings for Option<T> {
    fn more_optional(self) -> Option<Option<T>> {
        Some(self)
    }
}

fn main() {
    let x = Some(1);
    let y = x.more_optional();
    println!("{:?}", y);
}

对于flatten,我可能会这样写:

fn flatten<T>(opt: Option<Option<T>>) -> Option<T> {
    match opt {
        None => None,
        Some(v) => v,
    }
}

fn main() {
    let x = Some(Some(1));
    let y = flatten(x);
    println!("{:?}", y);
}

但是如果你想要一个特点:

trait MyThings<T> {
    fn flatten(self) -> Option<T>;
}

impl<T> MyThings<T> for Option<Option<T>> {
    fn flatten(self) -> Option<T> {
        match self {
            None => None,
            Some(v) => v,
        }
    }
}

fn main() {
    let x = Some(Some(1));
    let y = x.flatten();
    println!("{:?}", y);
}

有没有办法让flatten到任意深度?

参见如何展开任意嵌套的Option类型?


太棒了,非常感谢您提供的示例和and_then指针,这正是我想要的作为flat_map。是否有一种方法可以允许flatten到任意深度,例如对于Some(Some(Some(Some(3)))),还是我们必须为每个级别实现特性? - Jacob Brown
@kardeiz 这是一个有趣的问题。据我所知,没有处理任意类型嵌套的方法。我建议您开一个新的问题,因为我也想看到答案。^_^ - Shepmaster
@Shepmaster 我非常确定这需要负界限来实现,因为你需要一个适用于所有类型的特性,但对Option进行特殊处理。 - Dylan
我也在找flatmap的时候遇到了困难,所以我提出了一个拉取请求,希望将它包含在and_then的文档中:https://github.com/rust-lang/rust/pull/22111 - robinst

7
fn flatten<T>(x: Option<Option<T>>) -> Option<T> {
    x.unwrap_or(None)
}

在我的情况下,我正在处理一个在 unwrap_or_else 中返回 Option 的方法,却忘记了普通的 or_else 方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接