Implementation of the Expected idiom.
See the Andrei Alexandrescu’s talk (Systematic Error Handling in C++ and its slides.
Or more recent "Expect the Expected" by Andrei Alexandrescu for further background.
It is also inspired by C++'s proposed std::expected and Rust's Result.
Similar work is expectations by Paul Backus.
- lightweight, no other external dependencies
- works with
pure
,@safe
,@nogc
,nothrow
, andimmutable
- provides methods:
ok
,err
,consume
,expect
,expectErr
,andThen
,orElse
,map
,mapError
,mapOrElse
- type inference for ease of use with
ok
anderr
- allows to use same types for
T
andE
- allows to define
Expected
without value (void
forT
) - can be disabled with customHook
- provides facility to change the
Expected
behavior by customHook
implementation using the Design by introspection paradigm. - can enforce result check (with a cost)
- can behave like a normal
Exception
handled code by changing the usedHook
implementation - range interface
expected
uses adrdox to generate it's documentation. To build your own
copy, run the following command from the root of the expected
repository:
path/to/adrdox/doc2 --genSearchIndex --genSource -o generated-docs source
auto foo(int i) {
if (i == 0) return err!int("oops");
return ok(42 / i);
}
auto bar(int i) {
if (i == 0) throw new Exception("err");
return i-1;
}
// basic checks
assert(foo(2));
assert(foo(2).hasValue);
assert(!foo(2).hasError);
assert(foo(2).value == 21);
assert(!foo(0));
assert(!foo(0).hasValue);
assert(foo(0).hasError);
assert(foo(0).error == "oops");
// void result
assert(ok()); // no error -> success
assert(!ok().hasError);
// assert(err("foo").hasValue); // doesn't have hasValue and value properties
// expected from throwing function
assert(consume!bar(1) == 0);
assert(consume!bar(0).error.msg == "err");
// orElse
assert(foo(2).orElse!(() => 0) == 21);
assert(foo(0).orElse(100) == 100);
// andThen
assert(foo(2).andThen(foo(6)) == 7);
assert(foo(0).andThen(foo(6)).error == "oops");
// map
assert(foo(2).map!(a => a*2).map!(a => a - 2) == 40);
assert(foo(0).map!(a => a*2).map!(a => a - 2).error == "oops");
// mapError
assert(foo(0).mapError!(e => "OOPS").error == "OOPS");
assert(foo(2).mapError!(e => "OOPS") == 21);
// mapOrElse
assert(foo(2).mapOrElse!(v => v*2, e => 0) == 42);
assert(foo(0).mapOrElse!(v => v*2, e => 0) == 0);
See documentation for more usage examples.
If you're using dub, add the expected package to your project as a dependency.
Alternatively, since it's a single file self-contained implementation, you can simply copy expected.d
to your project source directory and compile as usual.
Build is tested against:
- dmd-latest
- dmd-2.095.1
- dmd-2.094.2
- dmd-2.091.1
- ldc-latest
- ldc-1.27.1
- ldc-1.25.1
- ldc-1.24.0