【问题标题】:How to implement some convenient methods (e.g., flat_map, flatten) on Option?如何在 Option 上实现一些方便的方法(例如 flat_map、flatten)?
【发布时间】:2015-02-17 16:45:54
【问题描述】:

如果 Rust 的 Option 提供一些额外的便利方法,例如 Option#flattenOption#flat_map,那就太好了,其中 flatten 会将 <Option<Option<T>> 减少到 Option<T>,而 flat_map 将像 @987654328 一样工作@,但采用返回 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&lt;T&gt; 附近的某处添加类型签名,但我不知所措......

希望这是有道理的,我为我不准确的术语道歉——我是 Rust 的新手。

【问题讨论】:

    标签: rust


    【解决方案1】:

    这些可能已经存在,就像您所期望的不同名称一样。检查docs for Option

    您将看到flat_map 更常见的是and_then

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

    做你想做的更大的方法是用你想要的方法声明一个trait,然后为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);
    }
    

    有没有办法让展平到任意深度

    How do I unwrap an arbitrary number of nested Option types?

    【讨论】:

    • 太棒了,非常感谢这些示例和指向and_then 的指针,这正是我想要的flat_map。有没有办法让flatten 达到任意深度,例如Some(Some(Some(Some(3)))),还是我们必须为每个级别实现特征?
    • @kardeiz 这是一个有趣的问题。据我所知,没有办法处理任意类型的嵌套。我建议提出一个新问题,因为我想自己看看答案。 ^_^
    • @Shepmaster 我很确定这将需要negative bounds 来实现,因为您需要一个为所有类型实现的特征,但要针对Options 进行特殊处理。
    • 我也很难找到平面图,这就是为什么在and_then 的文档中包含它的拉取请求:github.com/rust-lang/rust/pull/22111
    【解决方案2】:
    fn flatten<T>(x: Option<Option<T>>) -> Option<T> {
        x.unwrap_or(None)
    }
    

    就我而言,我正在处理unwrap_or_else 中的Option-returning 方法,而忘记了普通的or_else 方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-22
      • 2022-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-21
      • 2016-03-03
      相关资源
      最近更新 更多