10

How can I "add" a flatten() method to Option<U>, which would only typecheck if and only if U is an Option<T>, or more simply to add it to Option<Option<T>>? Naively, I am trying to write the following, which doesn't compile:

impl 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);
}
avernet
  • 30,895
  • 44
  • 126
  • 163

1 Answers1

10

You can't write inherent implementations (what you're trying) to types you didn't define, due to coherence (if you could, how do you know someone else hasn't also defined something called flatten?).

Instead, you need to define and implement a trait with the method you want. Anywhere you want the method, you use the trait:

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

impl<T> Flatten<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);
}

Also note that I changed the subject of the method from &self to self: you can't move out of a borrow (especially not an immutable one), so taking self by reference doesn't really make sense here.

DK.
  • 55,277
  • 5
  • 189
  • 162
  • 5
    Note that since rust 1.40 there is a [flatten](https://doc.rust-lang.org/std/option/enum.Option.html#method.flatten) function for `Option – proc Dec 29 '20 at 11:59