We can add a new trait ‘Optional’ that generalizes types that can be used for optional arguments.
pub trait Optional<T> {
fn none() -> Self;
fn some(val: T) -> Self;
fn to_option(self) -> Option<T>;
}
impl<T> Optional<T> for Option<T> {
fn none() -> Option<T> { None }
fn some(val: T) -> Option<T> { Some(val) }
fn to_option(self) -> Option<T> { self }
}
‘Optional’ is used in the desugar and solves generic issues. The desugar is attempted whenever the types do not match. This can be used in generics.
fn foo<T: Optional<U>, U: Default>(bar: T = Default::default()) { ... }
foo(x);
foo(..);
This desugars into
fn foo<T: Optional<U>, U: Default>(bar: T) {
let bar = match bar.to_option() {
None => { Default::default() }
Some(val) => val
};
...
}
foo(Optional::some(x));
foo(Optional::none());
With a concrete type and early return:
fn foo(bar: Option<uint> = return) { ... }
foo(2);
foo(..);
This desugars into
fn foo(bar: Option<uint>) {
let bar = match bar.to_option() {
None => { return }
Some(val) => val
};
...
}
foo(Optional::some(2));
foo(Optional::none());
With struct argument:
fn foo(Foo { bar = return }) { ... }
foo(bar);
foo(..);
This desugars into
fn foo(Foo { bar }: Foo) {
let bar = match bar.to_option() {
None => { return }
Some(val) => val
};
...
}
foo(Foo { bar: Optional::some(bar) });
foo(Foo { bar: Optional::none() });