Skip to content

Instantly share code, notes, and snippets.

@sinkuu
Last active August 29, 2015 14:05
Show Gist options
  • Save sinkuu/b9223102801089c0eae5 to your computer and use it in GitHub Desktop.
Save sinkuu/b9223102801089c0eae5 to your computer and use it in GitHub Desktop.
import std.traits;
struct Option(T)
{
private
{
void[T.sizeof] _storage;
bool _empty = true;
@property ref Unqual!T _payload() const @trusted
{
return *cast(Unqual!T*) _storage.ptr;
}
}
this(T val)
{
opAssign(val);
}
void opAssign(T val)
{
static if (is(T : Object))
_empty = val is null;
else
_empty = false;
_payload = val;
}
T get() const
{
assert(!_empty, "empty Option");
return _payload;
}
T getOrElse(lazy T defaultValue) const
{
return _empty ? defaultValue : _payload;
}
static if(is(T : Object))
{
T orNull() const
{
return _empty ? null : _payload;
}
}
void nullify()
{
assert(!_empty, "empty Option");
_empty = true;
static if (__traits(compiles, T.init)) _payload = T.init;
}
@property bool empty() const
{
return _empty;
}
@property T front() const
{
assert(!_empty, "empty Option");
return _payload;
}
alias popFront = nullify;
@property size_t length() const
{
return _empty ? 0 : 1;
}
}
///
pure @safe nothrow @nogc
unittest
{
Option!string val = "foo";
assert(!val.empty);
assert(val.get() == "foo");
// InputRange interface
import std.range;
static assert(isInputRange!(Option!int));
static assert(hasLength!(Option!int));
assert(val.front == "foo");
foreach (i; val) {}
import std.algorithm : equal;
assert(equal(val, only("foo")));
val.popFront();
foreach (i; val) assert(false);
}
pure @safe nothrow
unittest
{
Option!Object obj;
obj = null;
assert(obj.empty);
obj = new Object;
assert(!obj.empty);
}
pure @safe nothrow @nogc
unittest
{
// supports types which have no default values
static struct S
{
int x;
@disable this();
@disable @property static S init();
this(int num) pure @safe nothrow @nogc
{
x = num;
}
}
static assert(!__traits(compiles, S()));
static assert(!__traits(compiles, S.init));
Option!S s;
assert(s.empty);
s = S(100);
assert(!s.empty);
assert(s.get() == S(100));
s.nullify();
assert(s.empty);
Option!(immutable S) s2;
s2 = S(100);
assert(s2.get() == S(100));
}
pure @safe nothrow @nogc
unittest
{
immutable Option!string str = "foo";
assert(str.get() == "foo");
static assert(!__traits(compiles, str = "bar"));
}
Option!T option(T : Object)(T obj)
{
return obj is null ? Option!T() : Option!T(obj);
}
///
pure @safe nothrow
unittest
{
assert(!option(new Object).empty);
assert(option(cast(Object)null).empty);
}
Option!T option(T)(T val)
{
return Option!T(val);
}
Option!T option(T)()
{
return Option!T();
}
///
pure @safe nothrow @nogc
unittest
{
assert(!option(123).empty);
assert(option!int().empty);
}
auto optionSwitch(alias existFun, alias emptyFun = {}, T)(Option!T opt)
{
import std.functional : unaryFun;
if (opt.empty)
{
return emptyFun();
}
else
{
return unaryFun!existFun(opt.get());
}
}
///
pure @safe nothrow @nogc
unittest
{
Option!int opt;
opt.optionSwitch!(
(int x)
{
assert(false);
},
{
opt = 100;
});
assert(opt.get() == 100);
opt.optionSwitch!(
(int x)
{
assert(x == 100);
},
{
assert(false);
});
opt.optionSwitch!((x) { assert(x == 100); });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment