Как развернуть произвольное количество вложенных типов Option? - PullRequest
0 голосов
/ 22 сентября 2018

Я пытаюсь написать черту, которая позволит мне «развернуть» несколько вложенных 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());
}

Ответы [ 2 ]

0 голосов
/ 22 сентября 2018

Вместо того, чтобы выровнять вложенную опцию, как показывает другой ответ , я бы посоветовал вам никогда не создавать 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 - язык статической типизации;вы всегда будете знать точный уровень вложенности.

0 голосов
/ 22 сентября 2018

Я решил это с помощью автоматических черт (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());
}

детская площадка

...