Poor man's Result<T,E> implementation
Moritz Maxeiner via Digitalmars-d
digitalmars-d at puremagic.com
Fri Mar 3 17:45:17 PST 2017
Having taken a bit of time to get more familiar with Rust I
wondered if we could have something like Rust's algebraic result
type[1] using Phobos' Algebraic template and started to
experiment:
--- Usage
enum DivisionError
{
ZeroDivisor
}
Result!(int, DivisionError) divide(int dividend, int divisor)
{
mixin Result;
if (divisor == 0) { return err(DivisionError.ZeroDivisor); }
return ok(dividend / divisor);
}
// If you statically know that in your specific case the error
cannot
// happen you can just unwrap the Result to the contained
successful value
int valid = divide(15, 3).unwrap();
// This will throw an instance of Error, since unwrapping a Result
// that you cannot guarantee to actually be Ok is a logic bug.
int invalid = divide(15, 0).unwrap();
// Do some calculations of which some may fail
auto input = [ tuple( 15, 3),
tuple( -3, 0),
tuple( 3, 7)];
auto results = input.map!(i => divide(i.expand));
// Use case 1: Silently ignore failed calculations
results.save
.filter!(r => r.type == typeid(Ok!int))
.each!(tryVisit!(
(Ok!int result) => writeln(result),
() {}
));
// Use case 2: "Handle" them
results.save
.each!(Visit!(
(Ok!int result) => writeln(result),
(Err!DivisionError err) => writeln("Failed to divide: ", err)
));
---
--- Implementation
struct Ok(T) {
T v;
alias v this;
string toString() { return v.to!string; }
}
struct Err(E) {
E e;
alias e this;
string toString() { return e.to!string; }
}
alias Result(T, E) = Algebraic!(Ok!T, Err!E);
mixin template Result()
{
alias R = typeof(return);
alias T = typeof(__traits(getMember, R.AllowedTypes[0], "v"));
alias E = typeof(__traits(getMember, R.AllowedTypes[1], "e"));
R ok(T v) { return cast(R) Ok!T(v); }
R err(E e) { return cast(R) Err!E(e); }
}
@trusted nothrow
auto unwrap(R)(R r)
{
try {
return cast(typeof(__traits(getMember, R.AllowedTypes[0],
"v")))
r.get!(R.AllowedTypes[0])();
} catch (VariantException) {
throw new Error("Attempted to unwrap error");
} catch (Exception) {
// VariantN.get is supposed to only throw
VariantException. Bug?
assert(false);
}
}
import std.variant;
import std.conv;
import std.typecons;
import std.algorithm;
---
[1] https://doc.rust-lang.org/std/result/enum.Result.html
More information about the Digitalmars-d
mailing list