Как реализовать некоторые удобные методы (например, flat_map, flatten) в Option?

Было бы неплохо, если бы Rust's Option предоставлены некоторые дополнительные удобные методы, такие как Option#flatten а также Option#flat_map, где flatten уменьшит <Option<Option<T>> в Option<T>, а также flat_map будет работать как map, но принимает метод / замыкание, которое возвращает Option и выравнивает это.

flat_map довольно просто:

fn opt_flat_map< T, U, F: FnOnce(T) -> Option<U> >(opt: Option<T>, f: F) -> Option<U> {
  match opt {
    Some(x) => f(x),
    None => None
  }
}

flatten является более сложным, и я не знаю, как его определить. Это может выглядеть примерно так:

fn opt_flatten<T, U>(opt: Option<T>) -> Option<U> {
  match opt {
      Some( Some(x) ) => flatten_option( Some(x) ),
      _ => opt
  }
}

Но это, конечно, не работает. Какие-нибудь мысли?

Кроме того, как бы я пошел о реализации этих методов на Option enum, так что я могу использовать их изначально на Option пример? Я знаю, что мне нужно добавить сигнатуру типа где-то вокруг impl OptionExts for Option<T>, но я в растерянности...

Надеюсь, что это имеет смысл, и я прошу прощения за мою неточную терминологию - я новичок в Rust.

2 ответа

Решение

Они, вероятно, уже существуют, так же, как разные имена, чем вы ожидаете. Проверьте документы для варианта.

Вот увидишь flat_map как обычно and_then:

let x = Some(1);
let y = x.and_then(|v| Some(v + 1));

Лучший способ сделать то, что вы хотите, это объявить черту с методами, которые вы хотите, а затем реализовать его для Option:

trait MyThings {
    fn more_optional(self) -> Option<Self>;
}

impl<T> MyThings for Option<T> {
    fn more_optional(self) -> Option<Option<T>> {
        Some(self)
    }
}

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

За flattenЯ бы наверное написал:

fn flatten<T>(opt: Option<Option<T>>) -> Option<T> {
    match opt {
        None => None,
        Some(v) => v,
    }
}

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

Но если вы хотели черту:

trait MyThings<T> {
    fn flatten(self) -> Option<T>;
}

impl<T> MyThings<T> for Option<Option<T>> {
    fn flatten(self) -> Option<T> {
        match self {
            None => None,
            Some(v) => v,
        }
    }
}

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

Был бы способ позволить сгладить до произвольной глубины

См. Как развернуть произвольное количество вложенных типов Option?

fn flatten<T>(x: Option<Option<T>>) -> Option<T> {
    x.unwrap_or(None)
}

В моем случае я имел дело с Optionвозвратный метод в unwrap_or_else и забыл про равнину or_else метод.

Другие вопросы по тегам