ddn.var¶
A compact, dynamic value type for the D programming language.
Overview¶
The ddn.var module provides a versatile dynamic value type (var) that can hold values of many different types at runtime. It is designed to be compact, efficient, and easy to use for scenarios where static typing is impractical—such as configuration handling, JSON-like data structures, or scripting interfaces.
It serves as the core data structure for other DDN modules like ddn.config.ini, ddn.data.json5, and ddn.data.csv.
Features¶
Supported Types¶
The var type supports a comprehensive set of value types within a compact 24-byte footprint (on 64-bit systems):
| Type Category | Supported Types |
|---|---|
| Null | NULL — absence of a value |
| Boolean | BOOL — true or false |
| Integers | BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG |
| Floating Point | FLOAT, DOUBLE, REAL (stored as double internally) |
| Characters | CHAR, WCHAR, DCHAR |
| Strings | STRING — UTF-8 encoded strings |
| Collections | ARRAY — dynamic array of var, OBJECT — associative array (string → var) |
Type Conversion¶
Safe Conversion (as!T)¶
Use the as!T template to convert a var to a specific D type. If the conversion is impossible, it returns the default value for that type (T.init).
import ddn.var;
var v = 42;
int i = v.as!int; // Extract as int
string s = v.as!string; // Convert to string representation ("42")
var arr = [1, 2, 3];
var[] elements = arr.as!(var[]); // Extract array elements
Attempted Conversion (tryAs!T)¶
For scenarios where you need to distinguish between a missing/invalid value and a valid default value (like 0), use tryAs!T, which returns a Nullable!T.
import std.typecons : Nullable;
var v = "not a number";
auto result = v.tryAs!int;
if (result.isNull) {
// Conversion failed
} else {
// Use result.get
}
Object/Map Access¶
Access object fields using indexing or the convenient opDispatch syntax. var supports "auto-vivification" (automatic creation of nested objects) when assigning.
var obj;
obj["name"] = "Alice";
obj["stats"]["level"] = 10; // Automatically creates "stats" object
// Read values
string name = obj["name"].as!string;
// opDispatch syntax (field-like access)
obj.email = "alice@example.com";
int level = obj.stats.level.as!int;
Safe Navigation (getField)¶
To safely access nested fields without risking an assertion failure or creating unwanted entries, use getField.
// Returns 30 if "timeout" exists, otherwise 30 (default)
int timeout = config.getField("timeout", 30).as!int;
Array Operations¶
Work with dynamic arrays naturally using standard D syntax.
var arr = [1, 2, 3, 4, 5];
// Access by index
var first = arr[0];
// Slicing
var slice = arr[1 .. 3]; // [2, 3]
// Append
arr ~= 6;
// Iterate
foreach (ref el; arr.as!(var[])) {
// process element
}
Arithmetic Operations¶
Perform arithmetic on numeric var values. The result type is automatically promoted (e.g., int + double = double).
var a = 10;
var b = 3;
var sum = a + b; // 13
var diff = a - b; // 7
var quot = a / b; // 3 (integer division)
Note: Division by zero returns var.init (NULL) instead of crashing.
JSON Serialization¶
var acts as a JSON Document Object Model (DOM).
var data;
data["name"] = "example";
data["values"] = [1, 2, 3];
// Convert to JSON string
string json = data.toString();
// Output: {"name":"example","values":[1,2,3]}
// Write to OutputRange (efficient, no string allocation)
import std.array : appender;
auto buffer = appender!string;
data.toStringTo(buffer);
Deep Copy with dup()¶
Assignment of var values is shallow for arrays and objects (they share the underlying data). Use dup() for deep copies.
var original;
original["x"] = 1;
var shallow = original; // Points to same object
var deep = original.dup; // Independent copy
shallow["x"] = 2;
assert(original["x"].as!int == 2); // Changed
assert(deep["x"].as!int == 1); // Unchanged
Error Handling¶
By default, the ddn.var module follows a fail-fast strategy for runtime type misuse:
- Type Exceptions: Operations like indexing a non-object or using array APIs on non-arrays throw a
VarException(specificallyVarTypeExceptionorVarKeyException). This ensures that logic errors in data processing are caught early. - Null safety: Operations on
NULLtypes generally return valid defaults (e.g., empty array foras!(var[])) orfalse. - Arithmetic safety: Division or modulo by zero returns
var.init(NULL) instead of crashing or throwing.
For scenarios where you prefer automatic type conversion over exceptions, see Permissive Mode.
Permissive Mode (DDN_VAR_TYPE_COERCE)¶
If you prefer a more permissive, "JavaScript-like" behavior, you can compile the ddn library with the D version identifier DDN_VAR_TYPE_COERCE. When this mode is enabled:
- Auto-coercion: Mutating operations (like
v["key"] = valueorv ~= item) will automatically convert a scalarvarinto the required collection type (OBJECTorARRAY), overwriting its previous value. - Neutral read-only access: Read-only accessors (like const
v["missing"]) return aNULLvalue instead of throwing when there's a type mismatch or a missing key. - Warnings: In
debugbuilds, these automatic type changes emit warnings to the standard logger to help you track down unintended coercions.
Example (Permissive Mode)¶
// Compiled with -version=DDN_VAR_TYPE_COERCE
var v = 42;
v.name = "Alice"; // Auto-coerces v from INT to OBJECT
assert(v.type == var.Type.OBJECT);
var missing = v.nonexistent; // Returns Type.NULL instead of throwing
assert(missing.type == var.Type.NULL);
Thread Safety¶
vardoes not perform internal synchronization.- Concurrent mutation of the same instance is a data race.
- Independent copies can be used in different threads.