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

后端 未结 3 1424
悲哀的现实
悲哀的现实 2020-12-06 11:42

I\'m trying to write a trait that will allow me to "unwrap" multiple nested Option>>> to a single Option

相关标签:
3条回答
  • 2020-12-06 12:13

    Instead of flattening out the nested option, as the other answer shows, I'd advocate that you never create an Option<Option<T>> that you need to flatten in the first place. In the majority of cases I've seen, it's because someone misuses Option::map when they should have used 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)
    }
    

    Remember that Rust is a statically typed language; you will always know the exact level of nesting.

    See also:

    • Flatten nested Results in Rust
    0 讨论(0)
  • 2020-12-06 12:15

    If you have many Options and you want to avoid chaining unwraps, you can use match:

    let val = Some(Some(Some(5)));
    let res = match val {
        Some(Some(Some(v))) => v,
        _ => 0, // panic or default
    };
    
    0 讨论(0)
  • 2020-12-06 12:30

    I solved it with auto traits (optin_builtin_traits), but I'm not sure if this is the best approach:

    #![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

    0 讨论(0)
提交回复
热议问题