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.
A closed set of nine
Section titled “A closed set of nine” 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.
Why 32 bytes — and why a test guards it
Section titled “Why 32 bytes — and why a test guards 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.
See it for yourself
Section titled “See it for yourself”> output appears here — press Run
// quick check
Why is the Map variant wrapped in a Box?
An enum is as wide as its largest variant. Boxing the map shrinks that variant to a pointer, so the enum stays 32 bytes and a plain Integer cell doesn't pay for a map it doesn't hold.
Checkpoint
Section titled “Checkpoint”You understand the atom. Next: how the engine takes one apart safely — and why the compiler forces it to handle all nine cases.