Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

generics

rust

I'm trying to write a trait that will allow me to "unwrap" multiple nested Option<Option<...<T>>>> to a single Option<T> to better work with an API I am using. I'm trying to create a generic solution, but I can't figure out how to make it work.

This is one of my many attempts:

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>`
like image 413
slipheed Avatar asked Sep 22 '18 03:09

slipheed


People also ask

How do I force unwrap of a nil optional value?

If a nil optional is unwrapped, an error is thrown saying " Unexpectedly found nil while unwrapping an Optional value ." You're supposed to use forced unwrapping only in a pre-defined environment where you're certain that the optional value won't be nil. You can forcefully unwrap an optional using the exclamatory (!) operator like this:

What is the use of unwrapping in Swift?

Unwrapping in Swift is essentially verifying if the Optional value is nil or not, and then it performs a task only if it's not nil. You can perform unwrapping in the following ways: Using an if else block

Can I use tryparseoption in a union type?

NOTE: The tryParseOption code is just an example. A similar function tryParse is built into the .NET core libraries and should be used instead. Like other union types, option types have an automatically defined equality operation

What is the use of the option type?

The option type is widely used in the F# libraries for values that might be missing or otherwise invalid. For example, the List.tryFind function returns an option, with the None case used indicate that nothing matches the search predicate. [1;2;3;4] |> List.tryFind (fun x-> x = 3) // Some 3 [1;2;3;4] |> List.tryFind (fun x-> x = 10) // None


2 Answers

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
like image 125
Shepmaster Avatar answered Sep 28 '22 12:09

Shepmaster


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

like image 43
slipheed Avatar answered Sep 28 '22 12:09

slipheed