digitalmars.D - Poor man's Result<T,E> implementation
- Moritz Maxeiner (83/83) Mar 03 2017 Having taken a bit of time to get more familiar with Rust I
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
Mar 03 2017