Skip to content

Instantly share code, notes, and snippets.

@iK4tsu
Last active May 1, 2021 22:21
Show Gist options
  • Save iK4tsu/00cfdc5b8a216ef2f61393645da98bf8 to your computer and use it in GitHub Desktop.
Save iK4tsu/00cfdc5b8a216ef2f61393645da98bf8 to your computer and use it in GitHub Desktop.
Rust Result type implemented in D
module result;
import std.algorithm : cumulativeFold, each, map, sum;
import std.conv : to;
import std.exception : assumeWontThrow;
import std.range : chain, enumerate, iota, only, take, walkLength, zip;
import std.traits : isInstanceOf, ReturnType, Unqual;
import core.memory : pureMalloc;
struct Result(OkT, ErrT)
if (!is(OkT == void) && !is(ErrT == void))
{
static Result!(OkT, ErrT) Ok(OkT value)
{
Result!(OkT, ErrT) res;
res._okpayload = ok(value);
res._state = State.Ok;
return res;
}
static Result!(OkT, ErrT) Err(ErrT value)
{
Result!(OkT, ErrT) res;
res._errpayload = err(value);
res._state = State.Err;
return res;
}
bool isOk() const { return _state == State.Ok; }
bool isErr() const { return !isOk(); }
OkT except(in string msg)
{
if (isOk()) return okpayload._handle;
else failExpect(msg, errpayload.toString());
assert(0);
}
OkT unwrap()
{
if (isOk()) return okpayload._handle;
else assert(false, errpayload.toString());
}
ErrT exceptErr(in string msg)
{
if (isErr()) return errpayload._handle;
else failExpect(msg, okpayload.toString());
assert(0);
}
ErrT unwrapErr()
{
if (isErr()) return errpayload._handle;
else assert(false, okpayload.toString());
}
string toString()
{
return isOk
? "Ok("~okpayload.toString()~")"
: "Err("~errpayload.toString()~")";
}
private:
noreturn failExpect(in string msg, in string payload)
{
auto iter = only(msg, ": ", payload, "\0");
auto to = iter.map!"a.length".cumulativeFold!"a + b";
auto from = 1.iota.chain(to.take(3));
immutable len = iter.map!"a.length".sum;
auto str = (() @trusted pure nothrow @nogc => (cast(char*)(pureMalloc(len)))[0 .. len])();
zip(from, to, iter).each!(tp => str[tp[0] .. tp[1]] = tp[2]);
// asserts are not supposed to be catched
// if one does catch it, then it can lead to undefined behavior
// str will leak if this is catched
assert(false, str);
}
union {
ok _okpayload;
err _errpayload;
}
struct ok
{
string toString()()
{
static if (__traits(hasMember, OkT, "toString") && is(Unqual!(ReturnType!(OkT.toString)) == string))
return _handle.toString();
else
return _handle.to!string;
}
OkT _handle;
}
struct err
{
string toString()()
{
static if (__traits(hasMember, ErrT, "toString") && is(Unqual!(ReturnType!(ErrT.toString)) == string))
return _handle.toString();
else
return _handle.to!string;
}
ErrT _handle;
}
enum State
{
Ok,
Err,
}
auto okpayload() inout @property { return _okpayload; }
auto errpayload() inout @property { return _errpayload; }
State _state;
}
mixin template makeResult()
{
static assert (isInstanceOf!(Result, typeof(return)));
alias Ok = typeof(return).Ok;
alias Err = typeof(return).Err;
}
version(result_unittest)
{
import core.exception : AssertError;
import std.exception : assertThrown;
import std.typecons : Tuple, tuple;
struct NogcToString {
@safe pure nothrow @nogc
string toString() { return "Foo"; }
}
}
/// Result default behavior assert
@safe pure nothrow @nogc
unittest
{
Result!(string, string) res;
assert(res.isOk());
assert(res.unwrap() == "");
assert(res.except("This won't fail") == "");
}
/// Function nogc
@safe pure nothrow @nogc
unittest
{
Result!(Tuple!(), string) positive(int i)
{
if (i > 0) return Result!(Tuple!(), string).Ok(tuple());
else return Result!(Tuple!(), string).Err("Value was not positive!");
}
assert(positive(32).isOk());
assert(positive(32).unwrap() == tuple());
}
/// Function assert
@trusted pure
unittest
{
Result!(int, string) positive(int i)
{
if (i > 0) return Result!(int, string).Ok(i);
else return Result!(int, string).Err("Value was not positive!");
}
assert(positive(0).isErr());
assertThrown!AssertError(positive(0).unwrap());
}
/// Function with mixin makeResult
@safe pure nothrow @nogc
unittest
{
Result!(NogcToString, string) foo(bool b)
{
mixin makeResult;
if (b) return Ok(NogcToString());
else return Err("Failed in foo!");
}
assert(foo(true).isOk());
assert(foo(true).unwrap() == NogcToString());
}
/// Result toString
@safe pure
unittest
{
assert(Result!(int, char)().toString() == "Ok(0)");
assert(Result!(Result!(string, int), size_t)().toString() == "Ok(Ok())");
assert(Result!(int, char).Err(char.init).toString() == "Err(\xff)");
assert(Result!(Result!(string, int), size_t).Ok(Result!(string, int).Err(0)).toString() == "Ok(Err(0))");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment