Reputation: 7278
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>`
Upvotes: 21
Views: 7150
Reputation: 3057
If you have many Option
s and you want to avoid chaining unwrap
s, you can use match
:
let val = Some(Some(Some(5)));
let res = match val {
Some(Some(Some(v))) => v,
_ => 0, // panic or default
};
Upvotes: -1
Reputation: 430514
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:
Upvotes: 38
Reputation: 7278
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());
}
Upvotes: 6