ddn.result
ddn.result — value-based success/failure results.
This module provides Success, the empty payload for operations that succeed without returning a value, and Result, a thin wrapper around ddn_types, Either, ddn.types.Either representing "either a recoverable error or a successful outcome".
Results are ordinary values. Construction, copying and inspection are allocation-free and non-throwing when the relevant payload operations are; attributes are inferred from those operations. This suits low-level @nogc nothrow APIs as well as general application code.
Semantics:
- The success value is the first type argument and the recoverable
error is the second, following the conventional
Result!(Value, Error)order. - Payload types equal after
Unqualnormalization are rejected.Use distinct role wrappers for qualifier-distinguished types.
- Construction accepts exactly one of the payload types; implicit
conversions match neither constructor and must be made explicit by the caller.
- A default-constructed
ResultholdsE.init, sook()is false.There is no separate unset alternative: this is indistinguishable from explicitly storing
E.init, which may not be a meaningful domain error. - Accessing the inactive payload (
value()on an error state, orerror()on a success state) is a programming error. It terminates the process with a diagnostic onstderr— even in release builds — without allocating or throwing. Recoverable failures are represented by result values, never by this path. ok()reports which alternative is active. It never inspects errorcodes, null references or payload contents to guess success.
The branch-based accessors (ok, value, error) are the primary ergonomic API while D lacks native pattern matching; exhaustive handling remains available through match. With selective imports, import match explicitly and name the result before matching. Should language-level matching arrive, removing the accessors would still be an explicit, compatibility-reviewed deprecation — not an automatic cleanup.
Example:
import ddn.result : Result, Status, Success;
struct ParseError { string msg; size_t offset; }
struct Config { int value; }
struct UpdateError { enum Reason { NOT_FOUND, LOCKED } Reason reason; }
alias ParseResult = Result!(Config, ParseError);
alias UpdateStatus = Status!UpdateError;
ParseResult parseConfig(scope const(char)[] text) @safe pure nothrow @nogc {
if (text.length == 0)
return ParseResult(ParseError("empty input", 0));
return ParseResult(Config(1));
}
// Caller-side handling:
auto result = parseConfig("");
if (result.ok()) {
auto config = result.value();
} else {
auto failure = result.error();
}Design record: doc/DDN-RESULT.md in the repository documents the validated decisions behind this module.
Types 3
Successful completion without a payload.
Used as the success payload of Status: Status!SomeError means "either plain success or SomeError".
private ubyte _reservedReserved storage. The DMD-frontend x86-64 ABI classifier (LDC and DMD) treats a union whose member is an empty struct as memory class, which forces `Status!E` to be returned through a hidden pointe...Either a recoverable error or a successful outcome.
Result privately stores tagged error/value alternatives and adds unambiguous construction, branch-based access and a fail-fast misuse contract on top of the positional storage of ddn.types.Either. See the module documentation for the full semantics.
Parameters
V | the success payload type; Unqual!V must differ from Unqual!E. |
E | the recoverable failure payload type. |
private Either!(E, V) _eprivate _valueMisuseMsgDiagnostic for success-payload access on an error alternative.private _errorMisuseMsgDiagnostic for error-payload access on a success alternative.Result opAssign()(ref Result rhs) refReplace the active alternative by delegating to the storage's assignment.auto value()()Returns the success payload.auto error()()Returns the error payload.void _misuse(string msg)() static @nogc nothrow @trustedTerminates the process after writing a compile-time-constant diagnostic.Templates 1
Exhaustive handling over both alternatives, delegating to the positional Either.match core of ddn.types.
This is a free UFCS function so handlers may capture their enclosing scope. Selective imports must include match: import ddn.result: Result, match;. Alias one import if also using ddn.types.match in the same scope. Attributes depend on the handlers; capturing delegates can require GC allocation.
Only lvalue results are accepted: store a temporary in a named variable before matching. A returned reference or closure borrowing a payload must not outlive that result, and the payload must not be replaced while the borrow is in use. Taking the result by reference prevents matching from destroying a by-value parameter before the caller can use a returned borrow; it does not enforce the lifetime of borrows returned by handlers.
Parameters
handlers | callables covering both payload types; exact single-parameter types are required (after Unqual normalization). The first matching handler is used; both alternatives must be covered. |
result | lvalue result to inspect by reference without copying, preserving its qualifiers; it must remain alive while returned borrows are used. |