【问题标题】:How do I unwrap an arbitrary number of nested Option types?如何解开任意数量的嵌套选项类型?
【发布时间】:2018-09-22 03:14:24
【问题描述】:

我正在尝试编写一个特征,允许我将多个嵌套的 Option<Option<...<T>>>>“解包”为单个 Option<T>,以便更好地使用我正在使用的 API。我正在尝试创建一个通用解决方案,但我不知道如何使其工作。

这是我的众多尝试之一:

trait UnwrapOption<T> {
    fn unwrap_opt(self) -> Option<T>;
}

impl<T> UnwrapOption<T> for Option<T> {
    fn unwrap_opt(self) -> Option<T> {
        self
    }
}

impl<T> UnwrapOption<T> for Option<Option<T>> {
    fn unwrap_opt(self) -> Option<T> {
        match self {
            Some(e) => e.unwrap_opt(),
            None => None,
        }
    }
}

fn main() {
    let x = Some(Some(Some(1)));
    println!("{:?}", x.unwrap_opt());
}
error[E0282]: type annotations needed
  --> src/main.rs:22:24
   |
22 |     println!("{:?}", x.unwrap_opt());
   |                      --^^^^^^^^^^--
   |                      | |
   |                      | cannot infer type for type parameter `T` declared on the trait `UnwrapOption`
   |                      this method call resolves to `Option<T>`

【问题讨论】:

  • 如果你知道有 3 次嵌套,为什么不直接调用 unwrap() 链接 3 次呢? fn main() { let x = Some(Some(Some(1))); println!("{:?}", x.unwrap().unwrap().unwrap()); }
  • Options 可能在某些时候包含 None。例如:一些(无)或一些(一些(无))。这两个都应该映射到 None。
  • 我是 rust 新手,但理解你的意思。谢谢
  • 我建议不要将其命名为“unwrap”,因为这通常意味着 Rust 中的“panic on None”,而不是您在此处尝试实现的行为。

标签: generics rust


【解决方案1】:

我建议您不要创建首先需要展平的Option&lt;Option&lt;T&gt;&gt;,而不是像other answer shows那样将嵌套选项展平。在我见过的大多数情况下,这是因为有人误用了Option::map,而他们本应使用Option::and_then

fn main() {
    let input = user_input();

    let a = input.map(add1);
    // a is Option<Option<i32>>

    let b = input.and_then(add1);
    // a is Option<i32>
}

fn user_input() -> Option<i32> {
    Some(10)
}

fn add1(a: i32) -> Option<i32> {
    Some(a + 1)
}

请记住,Rust 是一种静态类型语言;您将始终知道嵌套的确切级别。

另见:

【讨论】:

  • 这实际上是我的问题 - 我没有意识到 and_then 是映射返回 Option 的事物的正确方法
【解决方案2】:

我用自动特征 (optin_builtin_traits) 解决了这个问题,但我不确定这是否是最好的方法:

#![feature(optin_builtin_traits)]

trait IsOption {}
impl<T> IsOption for Option<T> {}

auto trait IsSingleOption {}
impl<T> !IsSingleOption for Option<Option<T>> {}

trait UnwrapOption {
    type Inner;
    fn unwrap_opt(self) -> Option<Self::Inner>;
}

impl<T> UnwrapOption for Option<T>
where
    Self: IsSingleOption,
{
    type Inner = T;
    fn unwrap_opt(self) -> Option<Self::Inner> {
        self
    }
}

impl<T> UnwrapOption for Option<T>
where
    T: IsOption + UnwrapOption,
{
    type Inner = <T as UnwrapOption>::Inner;
    fn unwrap_opt(self) -> Option<Self::Inner> {
        match self {
            Some(e) => e.unwrap_opt(),
            None => None,
        }
    }
}

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

playground

【讨论】:

    【解决方案3】:

    如果你有很多Options,并且你想避免链接unwraps,你可以使用match

    let val = Some(Some(Some(5)));
    let res = match val {
        Some(Some(Some(v))) => v,
        _ => 0, // panic or default
    };
    

    【讨论】:

      猜你喜欢
      • 2018-10-21
      • 2021-02-10
      • 1970-01-01
      • 1970-01-01
      • 2019-05-07
      • 2015-03-15
      • 2018-11-04
      • 1970-01-01
      相关资源
      最近更新 更多