如何解开任意数量的嵌套Option类型?

时间:2018-09-22 03:14:24

标签: generics rust

我正在尝试编写一个特征,该特征将允许我将多个嵌套的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());
}

3 个答案:

答案 0 :(得分:7)

other answer shows一样,不要扁平化嵌套选项,我主张您永远不要创建首先需要扁平化的Option<Option<T>>。在我见过的大多数情况下,这是因为有人在应该使用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是一种静态类型的语言;您将永远知道嵌套的确切级别。

答案 1 :(得分:4)

我用自动特征(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

答案 2 :(得分:0)

如果有许多Option,并且希望避免链接unwrap,则可以使用match

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