Error Handling
17.1 Exceptions
class ValidationException : Exception
{
this(string msg, string file = __FILE__, size_t line = __LINE__)
{
super(msg, file, line);
}
}
void validate(int age)
{
if (age < 0) throw new ValidationException("age must be non-negative");
}
void main()
{
try
{
validate(-1);
}
catch (ValidationException e)
{
writeln("Invalid: ", e.msg, " at ", e.file, ":", e.line);
}
catch (Exception e)
{
writeln("Other: ", e.msg);
}
finally
{
writeln("cleanup");
}
}17.2 Throwable Hierarchy
Don't catch Errors in normal code. The program state is undefined afterwards. nothrow functions may still throw Errors.
Throwable
├── Exception (recoverable: ConvException, FileException, ErrnoException, ...)
└── Error (unrecoverable bugs: AssertError, RangeError, OutOfMemoryError, InvalidMemoryOperationError)17.3 Helper Functions
import std.exception;
enforce(x > 0, "x must be positive"); // throws Exception if false
auto f = enforce(fopen(...), "cannot open"); // returns the value if it's truthy
enforce!FileException(exists(path), "missing");
auto e = collectException(riskyOp()); // returns the exception (or null)
assertThrown!ConvException("abc".to!int); // in unittests
assertNotThrown(safeOp());
auto r = ifThrown("abc".to!int, 0); // default on exception17.4 assert and static assert
assert(x > 0, "x must be positive"); // removed in -release builds (except assert(0))
assert(0); // always halts: marks unreachable code
static assert(int.sizeof == 4, "unexpected int size"); // compile-time check17.5 Scope Guards
Scope guards run in reverse order. They're cleaner than nested try/finally.
void transfer()
{
lock();
scope(exit) unlock(); // always runs at scope exit
beginTransaction();
scope(failure) rollback(); // only if an exception propagates
scope(success) commit(); // only on normal exit
doWork();
}17.6 Error Handling Alternatives
Nullable!T / sentinel values, std.typecons.Expected-style libraries (e.g., expected on DUB), return codes in @nogc nothrow / BetterC code.