pub enum Value {
Show 27 variants
Int(i32),
Float(f32),
Bool(bool),
String(Arc<str>),
List(Arc<ListValue>),
DivertTarget(DefinitionId),
VariablePointer(DefinitionId),
TempPointer {
slot: u16,
frame_depth: u16,
},
Null,
FragmentRef(u32),
Array(Arc<Vec<Value>>),
Map(Arc<OrderedMap>),
Record {
shape: ShapeId,
fields: Arc<Vec<Value>>,
},
FnRef(DefinitionId),
Closure(Arc<ClosureValue>),
Handle {
kind: NameId,
id: u64,
},
Projection(Arc<ProjectionValue>),
OptionVal(Option<Arc<Value>>),
Range {
start: i32,
end: i32,
inclusive: bool,
},
Vec2(Vec2),
Vec3(Vec3),
Vec4(Vec4),
Quat(Quat),
Mat2(Mat2),
Mat3(Mat3),
Mat4(Mat4),
Weighted(Arc<WeightedValue>),
}Expand description
A runtime value in the ink VM.
Heap-allocating variants (String, List, Array, Map, Record,
Closure, Projection, OptionVal, Weighted) are wrapped in Arc so
that cloning a Value is always O(1) — a refcount bump, not a deep copy —
which makes call-frame cloning (during fork_thread) essentially free.
Atomic refcounts are used so Value can flow through Bevy’s parallel
scheduler.
This Arc wrapping is a performance mechanism under value
semantics, not reference semantics: sharing the underlying allocation
is unobservable (docs/value-model-spec.md §3), because every mutating
entry point forks the shared allocation on write (take → make_mut →
write-back — see docs/runtime-spec.md’s “Value model” section). A
binding that never observed a mutation never sees it, regardless of
whether it shares the Arc underneath.
The Array/Map collections follow the ratified value model
(docs/value-model-spec.md §4/§5): value semantics with copy-on-write
sharing. Sharing is unobservable (§3) — equality
is structural with an Arc::ptr_eq fast path, and mutation goes through
the take → make_mut → write-back RMW discipline
so an unshared collection mutates in place and a shared one performs a
single copy.
PartialEq is implemented by hand rather than derived so the collection
arms can take the ptr_eq shortcut; every scalar arm matches what the
derive would have produced.
Variants§
Int(i32)
Float(f32)
Bool(bool)
String(Arc<str>)
List(Arc<ListValue>)
DivertTarget(DefinitionId)
VariablePointer(DefinitionId)
A reference to a global variable, used for ref parameters.
TempPointer
A runtime-only pointer to a temp in a specific call frame.
Used for ref parameters that target temp variables.
Null
FragmentRef(u32)
A reference to a fragment in the output buffer’s fragment store. Fragments preserve structural output parts for locale re-rendering.
Array(Arc<Vec<Value>>)
An ordered, copy-on-write collection of values (value-model-spec §4).
The backing Vec is shared behind an Arc; clone is a refcount bump.
Mutation goes through array_make_mut, which
copies the backing vector only when the Arc is shared.
Map(Arc<OrderedMap>)
An insertion-ordered, copy-on-write map with scalar keys
(value-model-spec §4). Keys are int/string/bool (MapKey);
iteration order is insertion order, so the value is deterministic
without sorting or hashing.
Record
A closed-shape record (typed-dialect era, TM-4;
docs/value-model-spec.md §11c / docs/typed-mode-spec.md §6).
fields is a flat, shape-ordered vector shared behind an Arc — the
exact same COW discipline as Array/Map:
clone is a refcount bump, mutation goes through
record_make_mut. The shape is closed (no
dynamic add/remove of fields) and identified by ShapeId, an
interned index into the compiled story’s StructShapes table — two
records are never equal unless their shapes match, even if their
field vectors happen to coincide.
FnRef(DefinitionId)
A zero-bound function value (docs/t1c-spec.md §1/§6, wire tag
VAL_FN_REF). Partial application over a statically-named function
with no bound-arg prefix — #fn(name) where the target has no ref
params. The DefinitionId is the fn token (hashes from the target’s
name, so a saved token survives recompiles that only edit the body).
A no-payload scalar (Copy-cheap DefinitionId); no Arc needed.
Structural equality is “same fn token” (§5, sharing-unobservable).
Closure(Arc<ClosureValue>)
A function value with a bound-arg prefix (docs/t1c-spec.md
§1/§6, wire tag VAL_CLOSURE). Despite the name there is no lexical
environment — ink has no free variables; the “env” is the bound
prefix of the target’s declared params (a val entry snapshots the
value at creation, a ref entry captures a durable cell as a
VariablePointer).
Wrapped in Arc for the same O(1)-clone discipline as the collection
variants. Structural equality (§5): same fn token and equal env
rows — ref entries compare by bound cell, val entries by value,
both of which fall out of the derived per-entry comparison because a
ref payload is a VariablePointer (compared by its DefinitionId).
Handle
An opaque host-resource token (docs/t1d-spec.md §1/§2, wire tag
VAL_HANDLE). Host resources (entities, audio instances, assets,
timers) enter the script world as Handle tokens: {kind, id}
scalars with value semantics — copied like ints, serializable,
compared by token. No live pointer ever lives in a Value;
dereferencing happens only host-side, against the host’s registry,
inside bindings (the sharing-unobservable dogma applies verbatim — a
handle is a name, re-bound at a defined seam).
kind is a NameId into the compiled story’s manifest-declared
kind vocabulary (analyzer-side, not the format — the format ships
only the token shape, docs/format-v4-rfc.md §2). id is an opaque
u64 the host allocates; the script never interprets it.
A no-payload scalar (Copy-cheap: NameId + u64); no Arc
needed. Structural equality is token equality (kind == kind && id == id, §6) — no ordering exists (any </>/<=/>= is a runtime
fault in gradual mode, a compile error under the typed dialect), and
a handle is never a legal map key (the domain stays
int/string/bool — same restriction as function values).
No literal syntax and no dedicated opcode construct this value —
handles enter the script world only via bindings (docs/t1d-spec.md
§2 RULED). This variant, its wire encoding, and its .inkt atom
exist so a handle received from a binding can flow through globals,
collections, saves, and the transcript like any other value.
Fields
Projection(Arc<ProjectionValue>)
A symbolic path projection (docs/t1e-spec.md §1/§3, wire tag
VAL_PROJECTION): (root cell, path segments) — never an interior
pointer. Created only in ref-argument position (ref npc.inventory[3]); reads walk the path against the root’s current
value, writes desugar to root-cell RMW (take → walk → make_mut
spine → write → store back, spec §3). The segment list is fixed at
creation (“index expressions snapshot at ref creation”, spec §1) —
there is no lazy re-evaluation.
Wrapped in Arc for the same O(1)-clone discipline as
Closure. Structural equality (spec §4 PROPOSED,
implemented here): same root cell and equal segments.
OptionVal(Option<Arc<Value>>)
A typed-absence value — the compiler-owned Option[T] enum (NS-A1,
docs/stdlib-spec.md §1.1/§1.4, ruled 2026-07-18: “a fault says
‘your program is wrong’; Option says ‘the world didn’t have one’”).
Option[T] is the third compiler-known parameterized builtin
(joining [T]/[K: V]) — a compiler-owned enum shape, NOT user
generics. Runtime representation: None is payload-free (no
allocation); Some wraps its inner value behind an Arc so clone
stays O(1) like every other heap-bearing variant. Nesting is legal
and meaningful (some(none) != none — the wire and equality both
preserve it).
Named OptionVal (not Option) purely to avoid the eternal
core::option::Option shadowing hazard inside match arms; the
ValueType discriminant and every author-facing surface still
say “option”.
Structural equality: none == none; some(x) == some(y) iff
x == y; an Option is never equal to a bare T (the ruled
Option[T] ≠ T strictness holds at the value layer too). Display
(stringify/string(x)): none / some(<inner>) — the boring,
stable form, total forever (F28). The §1.6b display-boundary
forgiveness (Track B4) is a brink-runtime-only concern layered on
top at read time (value_ops::stringify_display) — deliberately
not implemented at this value-definition layer, since string()
and every non-display consumer must keep seeing the total form.
Range
An integer range value (NS-A5, docs/stdlib-spec.md §7 — F7, ruled
2026-07-19: “ranges are a REAL Value kind”). start..end (exclusive)
or start..=end (inclusive) over int bounds — v1 is int-only.
Ranges join the closed iterable set (for i in 0..n), index like a
virtual array of their elements, and are the substrate of the
language’s first value refinement (the inhabited range consumed by
rand::int). A durable wire form exists (VAL_RANGE) because
FlowFrame spills for-loop iterators across await — a range held
in a loop snapshot must survive save/load.
A no-payload scalar (two i32s + a flag); no Arc needed. The
written form is preserved — 1..7 and 1..=6 keep their
inclusive flag through saves, the transcript, and display — but
equality is content equality (F7’s ruling word): two ranges are
equal iff they denote the same integer sequence, so 1..=6 == 1..7
and every empty range equals every other empty range. This is the
same content-over-form posture as the #909 map-equality ruling
(insertion order iterates, content compares).
Fields
Vec2(Vec2)
A 2-lane f32 vector (NS-A8, docs/tower-mini-spec.md T1: the tower
value kinds are glam-backed — glam is the in-memory compute type,
so vector/quaternion/matrix ops arrive correct-by-construction).
Serde discipline (T5): the derive on Value routes every tower
variant through the hand-written lane modules in [tower_serde] —
explicit x, y(, z, w) lane order for vectors and the quat,
column-major column-by-column for matrices — NEVER glam’s memory
representation (which varies with SIMD features and versions) and
never glam’s own serde feature (kept off in Cargo.toml).
Equality (T4): componentwise IEEE via glam’s derived PartialEq — a
NaN-bearing vector never equals itself, -0.0 == +0.0 per lane,
exactly like bare Float. Tower values are NOT orderable (§4b: a
vector in an ordering context is a NotOrderable fault) and are
never legal map keys (MapKey::from_value has no tower arms).
Vec3(Vec3)
A 3-lane f32 vector (NS-A8). The unaligned glam::Vec3 (not
Vec3A) per the mini-spec — aligned variants would bloat every
Value. See Vec2 for the shared tower discipline.
Vec4(Vec4)
A 4-lane f32 vector (NS-A8). See Vec2.
Quat(Quat)
A rotation quaternion (NS-A8), lane order (x, y, z, w) per glam
(T3: conventions per glam, wholesale — right-handed, quat * quat
composes, quat * vec rotates). See Vec2.
Mat2(Mat2)
A column-major 2×2 f32 matrix (NS-A8, T2: all matrix sizes ship).
See Vec2.
Mat3(Mat3)
A column-major 3×3 f32 matrix (NS-A8). The unaligned glam::Mat3
(not Mat3A). See Vec2.
Mat4(Mat4)
A column-major 4×4 f32 matrix (NS-A8). See Vec2.
Weighted(Arc<WeightedValue>)
A weighted table (NS-A7, docs/stdlib-spec.md §8): Weighted[T] —
positive-int weights over values, in construction order.
Evidence-by-construction: the only producer (weighted_new)
refuses empty tables and non-positive/non-int weights, so a
Weighted that exists is always a valid roll target (total). The
entry row is a multiset (F17: duplicate weights legal and
meaningful — deliberately divergent from Map’s key-set). v1 is
construct-and-roll: no len, no iteration, no mutation.
Implementations§
Source§impl Value
impl Value
Sourcepub fn value_type(&self) -> ValueType
pub fn value_type(&self) -> ValueType
Return the type discriminant for this value.
Sourcepub fn range(start: i32, end: i32, inclusive: bool) -> Self
pub fn range(start: i32, end: i32, inclusive: bool) -> Self
Build a Range from its written bounds.
Sourcepub fn as_range(&self) -> Option<(i32, i32, bool)>
pub fn as_range(&self) -> Option<(i32, i32, bool)>
Borrow the (start, end, inclusive) triple if this value is a
Range.
Sourcepub fn range_end_exclusive(&self) -> Option<i64>
pub fn range_end_exclusive(&self) -> Option<i64>
The one-past-the-last element bound of a Range,
normalized over the written form (i64 so 1..=i32::MAX cannot
overflow). None for any non-range value.
Sourcepub fn range_len(&self) -> Option<i64>
pub fn range_len(&self) -> Option<i64>
Number of elements a Range denotes (0 for an empty
range — a range never has negative length). None for any non-range
value. i64 because i32::MIN..=i32::MAX has 2³² elements.
Sourcepub fn weighted(entries: Vec<(i32, Value)>) -> Self
pub fn weighted(entries: Vec<(i32, Value)>) -> Self
Build a Weighted table from (weight, value)
entries. The caller owns the §8 evidence-by-construction invariant
(non-empty, positive weights) — the VM’s weighted_new op and the
wire reader both validate before calling this.
Sourcepub fn as_option(&self) -> Option<Option<&Value>>
pub fn as_option(&self) -> Option<Option<&Value>>
Borrow the Option payload if this value is an
OptionVal: Some(Some(&inner)) for some(x),
Some(None) for none, None for any non-Option value.
Sourcepub fn as_int(&self) -> Option<i32>
pub fn as_int(&self) -> Option<i32>
Extract an i32 if this value is an Int.
Strict: does not coerce floats or booleans. Returns None for any
other variant. For binding authors that want to read an integer
argument from ink.
Sourcepub fn as_bool(&self) -> Option<bool>
pub fn as_bool(&self) -> Option<bool>
Extract a bool if this value is a Bool.
Strict: does not treat nonzero numbers as truthy. Use the VM’s own truthiness rules if you need ink-style coercion.
Sourcepub fn map(map: OrderedMap) -> Self
pub fn map(map: OrderedMap) -> Self
Build a Map from an OrderedMap.
Sourcepub fn record(shape: ShapeId, fields: Vec<Value>) -> Self
pub fn record(shape: ShapeId, fields: Vec<Value>) -> Self
Build a Record from a shape id and its field values
(already in the shape’s declared field order — the caller, not this
constructor, is responsible for ordering).
Sourcepub fn as_record(&self) -> Option<(ShapeId, &Arc<Vec<Value>>)>
pub fn as_record(&self) -> Option<(ShapeId, &Arc<Vec<Value>>)>
Borrow the record payload if this value is a Record.
Sourcepub fn closure(target: DefinitionId, env: Vec<ClosureEnvEntry>) -> Self
pub fn closure(target: DefinitionId, env: Vec<ClosureEnvEntry>) -> Self
Build a Closure from a target and its bound-arg
prefix (T1c, docs/t1c-spec.md §6).
Sourcepub fn fn_target(&self) -> Option<DefinitionId>
pub fn fn_target(&self) -> Option<DefinitionId>
The fn token (target DefinitionId) if this value is a function
value — FnRef or Closure.
Sourcepub fn as_closure(&self) -> Option<&Arc<ClosureValue>>
pub fn as_closure(&self) -> Option<&Arc<ClosureValue>>
Borrow the closure payload if this value is a Closure.
Sourcepub fn as_weighted(&self) -> Option<&Arc<WeightedValue>>
pub fn as_weighted(&self) -> Option<&Arc<WeightedValue>>
Borrow the weighted-table payload if this value is a
Weighted.
Sourcepub fn handle(kind: NameId, id: u64) -> Self
pub fn handle(kind: NameId, id: u64) -> Self
Build a Handle token from a kind and host-allocated
id (T1d, docs/t1d-spec.md §2). Note: this constructor is a plain
value builder, not a capability mint — the invariant that handles
only ever originate from a binding is enforced by the compiler (no
literal syntax, no opcode constructs this value), not by this type.
Sourcepub fn as_handle(&self) -> Option<(NameId, u64)>
pub fn as_handle(&self) -> Option<(NameId, u64)>
Borrow the (kind, id) pair if this value is a Handle.
Sourcepub fn projection(cell: DefinitionId, segments: Vec<ProjSegment>) -> Self
pub fn projection(cell: DefinitionId, segments: Vec<ProjSegment>) -> Self
Build a Projection from a root cell and its
ordered segment chain (docs/t1e-spec.md §1/§3).
Sourcepub fn as_projection(&self) -> Option<&Arc<ProjectionValue>>
pub fn as_projection(&self) -> Option<&Arc<ProjectionValue>>
Borrow the projection payload if this value is a
Projection.
Sourcepub fn as_array(&self) -> Option<&Arc<Vec<Value>>>
pub fn as_array(&self) -> Option<&Arc<Vec<Value>>>
Borrow the array payload if this value is an Array.
Read-only: the returned slice never triggers a copy. Mutation uses
array_make_mut.
Sourcepub fn as_map(&self) -> Option<&Arc<OrderedMap>>
pub fn as_map(&self) -> Option<&Arc<OrderedMap>>
Borrow the map payload if this value is a Map.
Read-only: mutation uses map_make_mut.
Sourcepub fn array_make_mut(&mut self) -> Option<&mut Vec<Value>>
pub fn array_make_mut(&mut self) -> Option<&mut Vec<Value>>
Copy-on-write mutable access to an Array’s backing
vector, or None for any other value.
This is the make_mut step of the take → make_mut → write-back RMW
discipline (value-model-spec §5). When the backing Arc is unique the
mutation is in place (O(1) amortized append); when it is shared with a
snapshot or another slot, exactly one O(n) copy is made and the value
becomes unique again. Because sharing is unobservable (§3), callers
cannot tell which path was taken.
Sourcepub fn map_make_mut(&mut self) -> Option<&mut OrderedMap>
pub fn map_make_mut(&mut self) -> Option<&mut OrderedMap>
Copy-on-write mutable access to a Map’s contents, or
None for any other value. See array_make_mut
for the RMW discipline this implements.
Sourcepub fn record_make_mut(&mut self) -> Option<&mut Vec<Value>>
pub fn record_make_mut(&mut self) -> Option<&mut Vec<Value>>
Copy-on-write mutable access to a Record’s flat
field vector, or None for any other value. See
array_make_mut for the RMW discipline this
implements — the shape itself never changes (closed shape), only
field values.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Value
impl<'de> Deserialize<'de> for Value
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for Value
Structural equality with an Arc::ptr_eq fast path for collections
(value-model-spec §4/§5).
impl PartialEq for Value
Structural equality with an Arc::ptr_eq fast path for collections
(value-model-spec §4/§5).
Every scalar arm reproduces exactly what #[derive(PartialEq)] would emit.
The Array/Map arms add the ptr_eq shortcut: two values that share the
same Arc (the same snapshot) compare equal immediately, otherwise the
comparison is element-wise structural. NaN-bearing collections that are
not the same snapshot never compare equal, because f32 equality
composes structurally through the elements; a collection compared against
itself (same Arc) is equal even if it contains a NaN — the spec calls
this out as harmless and stated (§4).