Option
Option represents the union of two types - Some<T>
and None
. An Option<T>
is an extension type of T?
. Therefore, Option
has zero runtime cost and has one big advantage over T?
, you can chain null specific operations!
rust_core support nullable and Option
implementations of classes and methods for ergonomic convenience where possible, but you
can easily switch between the two with toOption
and toNullable
(or you can use .v
directly).
Usage
The Option
Type and features work very similar to Result. We are able to chain operations in a safe way without
needing a bunch of if
statements to check if a value is null.
Option<int> intOptionFunc() => None;
double halfVal(int val) => val/2;
Option<double> val = intOptionFunc()
.map(halfVal);
expect(val.unwrapOr(2), 2);
See the docs for all methods and extensions.
You can also use Option in pattern matching
switch(Some(2)){
case Some(:final v):
// do something
default:
// do something
}
or
final x = switch(Some(2)){
Some(:final v) => "some"
_ => "none"
}
Early Return Key Notation
Option also supports "Early Return Key Notation" (ERKN), which is a derivative of "Do Notation". It allows a
function to return early if the value is None
, and otherwise safely access the inner value directly without needing to unwrap or type check.
Option<int> intNone() => None;
Option<double> earlyReturn(int val) => Option(($) { // Early Return Key
// Returns here
double x = intNone()[$].toDouble();
return Some(val + x);
});
expect(earlyReturn(2), None);
This is a powerful concept and make you code much more concise without losing any safety.
For async, use Option.async
e.g.
FutureOption<double> earlyReturn() => Option.async(($) async {
...
});
To Option or Not To Option
As mentioned Option<T>
is an extension type of T?
so they can be used interchangeably with no runtime cost.
Option<int> intNone() => None;
Option<int> option = intNone();
int? nullable = option.v;
nullable = option.toNullable(); // or
nullable = option as int?; // or
option = Option.from(nullable);
option = nullable as Option<int>; // or
If Dart already supports nullable types, why use an option type? - with null, chaining null specific operations is not possible and the only alternate solution is a bunch of if statements and implicit and explicit type promotion. The Option
type solves these issues.
final x;
final y;
switch(optionFunc1().map((e) => e + " added string").zip(optionFunc2())){
case Some(:final v):
(x, y) = v;
default:
return
}
// use x and y
or if using early return notation.
final (x,y) = optionFunc1().map((e) => e + " added string").zip(optionFunc2())[$];
// use x and y
vs
final x = optionFunc1();
if (x == null) {
return;
}
else {
x = x + " added string";
}
final y = optionFunc2();
if (y == null) {
return;
}
// use x and y
With Option
you will also never get another null assertion error again.
As in the previous example, it is recommended to use the Option
type as the return type, since it allows
early return and chaining operations. But the choice is up to the developer.