pub struct Value(/* private fields */);Expand description
A runtime value, packed into a single 64-bit word by NaN-boxing.
A dynamic interpreter spends most of its time moving values between the stack,
locals, and the operand of an instruction. Representing each one as a Rust
enum costs sixteen bytes (a tag word plus the widest payload) and a branch on
every copy. NaN-boxing folds the kind and the payload into the bit pattern of
one f64-sized word, so a Value is Copy, eight bytes wide, and needs no
discriminant alongside it.
The trick is that IEEE-754 leaves a large block of bit patterns unused: every
quiet-NaN encoding names the same abstract “not a number”. A Value stores a
real f64 as itself, and hides every other kind — nil,
booleans, 32-bit integers, and interned Symbols — inside quiet-NaN payloads
that no genuine float ever produces. Reading a value back is a mask and a
compare; see unpack.
The encoding is entirely safe: it is built from f64::to_bits and
f64::from_bits and integer arithmetic, with no pointers and no unsafe.
Because it never boxes a pointer, it carries no heap data — strings and other
interned identities travel as Symbol handles, resolved elsewhere against the
interner that issued them.
§Equality
Equality follows IEEE-754 for floats and identity for everything else: two
Float values compare with f64 semantics, so NaN != NaN
and 0.0 == -0.0; all other kinds (including a Float against a non-Float)
compare by bit pattern. Because NaN != NaN, Value deliberately does not
implement Eq or Hash; if you need a hashable key, branch on
unpack and hash the parts, or use bits when
you have separately ensured no float is NaN.
§Examples
use value_lang::{Unpacked, Value};
let answer = Value::int(42);
let ratio = Value::float(0.375);
assert!(answer.is_int());
assert_eq!(answer.as_int(), Some(42));
assert_eq!(ratio.as_float(), Some(0.375));
// Branch on the kind with `unpack`.
match answer.unpack() {
Unpacked::Int(n) => assert_eq!(n, 42),
_ => unreachable!(),
}
// A `Value` is eight bytes and `Copy`.
assert_eq!(core::mem::size_of::<Value>(), 8);Implementations§
Source§impl Value
impl Value
Sourcepub const fn nil() -> Self
pub const fn nil() -> Self
The unit value, nil — the absence of any other value.
This is what an interpreter yields for an expression with no result, an
uninitialised local, or a missing map entry. It is also Value::default.
§Examples
use value_lang::Value;
let v = Value::nil();
assert!(v.is_nil());
assert_eq!(v, Value::default());Sourcepub const fn bool(b: bool) -> Self
pub const fn bool(b: bool) -> Self
A boolean value.
§Examples
use value_lang::Value;
assert_eq!(Value::bool(true).as_bool(), Some(true));
assert_eq!(Value::bool(false).as_bool(), Some(false));Sourcepub const fn int(n: i32) -> Self
pub const fn int(n: i32) -> Self
A 32-bit signed integer.
Integers are stored as an i32 because that is what fits losslessly beside
the tag in a NaN-box payload; use float when you need the
full magnitude and precision range of a double.
§Examples
use value_lang::Value;
assert_eq!(Value::int(-7).as_int(), Some(-7));
assert_eq!(Value::int(i32::MAX).as_int(), Some(i32::MAX));Sourcepub fn float(f: f64) -> Self
pub fn float(f: f64) -> Self
A floating-point value.
Any finite double, both infinities, and NaN are accepted. Every NaN is
stored as one canonical bit pattern, so a round trip through Value
normalises NaN payloads (the value is still NaN, and still compares
unequal to itself).
§Examples
use value_lang::Value;
assert_eq!(Value::float(2.5).as_float(), Some(2.5));
assert_eq!(Value::float(f64::INFINITY).as_float(), Some(f64::INFINITY));
assert!(Value::float(f64::NAN).as_float().unwrap().is_nan());Sourcepub fn sym(s: Symbol) -> Self
pub fn sym(s: Symbol) -> Self
An interned Symbol — a compact handle for a string or identifier.
The symbol’s 32-bit id is packed directly into the value. It is only
meaningful with the interner that issued it; Value stores the handle, not
the bytes.
§Examples
use intern_lang::Interner;
use value_lang::Value;
let mut interner = Interner::new();
let name = interner.intern("total");
let v = Value::sym(name);
assert_eq!(v.as_sym(), Some(name));
assert_eq!(interner.resolve(v.as_sym().unwrap()), Some("total"));Sourcepub fn is_float(self) -> bool
pub fn is_float(self) -> bool
Returns true when this value is a float.
Every value that is not a boxed kind is a float, including the infinities
and NaN.
Sourcepub fn as_bool(self) -> Option<bool>
pub fn as_bool(self) -> Option<bool>
Returns the boolean, or None if this value is not a boolean.
Sourcepub fn as_int(self) -> Option<i32>
pub fn as_int(self) -> Option<i32>
Returns the integer, or None if this value is not an integer.
Sourcepub fn as_float(self) -> Option<f64>
pub fn as_float(self) -> Option<f64>
Returns the float, or None if this value is not a float.
This does not convert an int to a float; it returns
None for every non-float kind. Convert explicitly if you want coercion.
Sourcepub fn as_sym(self) -> Option<Symbol>
pub fn as_sym(self) -> Option<Symbol>
Returns the interned Symbol, or None if this value is not a symbol.
Sourcepub const fn bits(self) -> u64
pub const fn bits(self) -> u64
Returns the raw 64-bit NaN-box encoding.
This is the exact bit pattern the value occupies. It is stable for a given
value and useful for building a custom hash or a compact serialization, but
note that two NaN floats share one canonical pattern and 0.0/-0.0 do
not — so raw bits are identity, not numeric equality.
Sourcepub fn unpack(self) -> Unpacked
pub fn unpack(self) -> Unpacked
Expands this value into its Unpacked tagged-union form for matching.
This is the reverse of Value::from(unpacked). Use it when you need to
branch on every kind at once rather than test one kind with an as_*
accessor.
§Examples
use value_lang::{Unpacked, Value};
fn describe(v: Value) -> &'static str {
match v.unpack() {
Unpacked::Nil => "nil",
Unpacked::Bool(_) => "bool",
Unpacked::Int(_) => "int",
Unpacked::Float(_) => "float",
Unpacked::Sym(_) => "sym",
}
}
assert_eq!(describe(Value::int(1)), "int");
assert_eq!(describe(Value::nil()), "nil");Trait Implementations§
impl Copy for Value
Source§impl<'de> Deserialize<'de> for Value
Available on crate feature serde only.
impl<'de> Deserialize<'de> for Value
serde only.