Skip to content

Enums & the Value type

In Phase 1 you met Value as “a cell — one of nine shapes.” That was the shallow pass. Now we go deep on the most important type in the engine: why the set of shapes is closed, and what it costs in memory. Both answers are deliberate engineering, and both show up in real tests.

You’ll be able to: explain why Value is a closed enum, and why its size is pinned at 32 bytes by a test that fails if anyone makes it bigger.

clinker-record ·value.rs ·Value type @47d2e12
pub enum Value {
Null,
Bool(bool),
Integer(i64),
Float(f64),
String(FieldStr),
Date(NaiveDate),
DateTime(NaiveDateTime),
Array(Vec<Value>),
Map(Box<IndexMap<Box<str>, Value>>),
}

A Rust enum is a sum type: a value is exactly one variant at a time, and the compiler knows the complete list. There is no tenth shape, and there can never be one without editing this declaration. That closedness is the point. Clinker’s expression language, CXL, runs a formula over every record — and CXL can be typechecked before a job runs (a typo fails at plan time, not mid-file) precisely because the set of value shapes is fixed and known in advance. The closed enum is that shared, finite vocabulary; the language, the format layer, the planner, and the runtime all rely on it.

A Record is a Vec<Value>. Every cell of every row is a Value, so the size of one Value multiplies across all your data. An enum is as wide as its largest variant (plus a small tag), so a single bloated variant would tax every cell.

Look closely at the last variant:

Map(Box<IndexMap<Box<str>, Value>>),

An IndexMap is a big struct. If it were stored inline, every Value — even a plain Integer — would be as wide as a map. So the map is Boxed: the variant holds just an 8-byte pointer, and the enum’s width is set by the genuinely small variants instead. The result is pinned by a test:

clinker-record ·value.rs ·test_value_enum_size test @47d2e12
#[test]
fn test_value_enum_size() {
assert_eq!(std::mem::size_of::<Value>(), 32);
}

Value is 32 bytes, and that test fails the build if a change makes it bigger. Size is treated as a contract, not an accident.

rust // editable

// quick check

Why is the Map variant wrapped in a Box?

You understand the atom. Next: how the engine takes one apart safely — and why the compiler forces it to handle all nine cases.