Qubit Value
qubit-value gives Rust applications one type-safe boundary for values that are
known only at runtime. It is useful when configuration, metadata, protocol
fields, or user input can be a boolean, number, string, date, collection, or
structured JSON value, but the application still needs explicit types,
controlled conversion, and predictable errors.
The problem it solves
Without a shared runtime value model, each key-value subsystem tends to invent
its own enum, conversion rules, unset semantics, and serialization format.
That creates three recurring problems:
- a missing value, an explicitly empty collection, and JSON
nullare easily confused; - a one-item collection can be accidentally treated as a scalar;
- values crossing a process or storage boundary lose their runtime type, or accept conversions that were never intended.
Value stores one typed scalar, MultiValues stores one homogeneous
collection, and ValueContainer preserves the explicit scalar-or-collection
shape. Unset(DataType) retains the declared type without pretending that a
concrete value exists.
Quick start: a small runtime configuration map
This is a small configuration-like map: each key stores a different Value,
then the reader chooses strict access, explicit conversion, or a typed default.
The snippet assumes it is inside a function that returns a compatible Result,
so ? can propagate value errors.
use HashMap;
use Duration;
use DataType;
use Value;
let config = from;
let host: String = config.get?;
assert_eq!;
let port: u16 = config.to?;
assert_eq!;
let debug: bool = config.get?;
assert!;
let timeout: Duration = config.get_or?;
assert_eq!;
If you need a complete, general-purpose configuration object instead of
assembling a map yourself, use Config from
rs-config. It builds on Value and
adds higher-level capabilities such as property management, typed and
multi-value reads, defaults, sections, conversion policies, interpolation, and
pluggable file/environment configuration sources.
get() is a strict type read: it does not silently convert. to() uses the
shared conversion rules from qubit-datatype; failed conversions remain
errors. The converter feature is required for to() and to_or(); get_or()
only supplies a fallback for an unset value and does not convert.
Installation
Add the core crate and its type vocabulary to Cargo.toml:
[]
= "0.10"
= { = "0.10", = false }
The default feature set is empty. Enable only the families you use:
| Feature | Additional DataType or capability |
|---|---|
converter |
Cross-type conversion APIs such as Value::to |
chrono |
Date, Time, DateTime, and Instant |
big-integer |
BigInteger backed by num_bigint::BigInt |
big-decimal |
BigDecimal backed by bigdecimal::BigDecimal |
big-number |
Compatibility alias for both big-number features |
url |
Url backed by url::Url |
json |
Json backed by serde_json::Value and bounded JSON Wire decoding; Natural JSON also requires converter |
redact |
Policy-aware redacted views through qubit-redact |
all |
converter, chrono, big-number, url, json, and redact |
Supported DataType values
qubit_datatype::DataType is the closed runtime type vocabulary used by
Value, MultiValues, and Unset. The same type identifies a scalar and its
homogeneous collection form.
DataType |
Rust value | Feature | Typical use |
|---|---|---|---|
Bool |
bool |
— | flags and switches |
Char |
char |
— | one Unicode character |
Int8 / Int16 / Int32 / Int64 / Int128 |
i8 … i128 |
— | signed integers |
UInt8 / UInt16 / UInt32 / UInt64 / UInt128 |
u8 … u128 |
— | unsigned integers |
Float32 / Float64 |
f32 / f64 |
— | finite or in-memory floating-point values |
String |
String |
— | text and text-backed input |
Date |
chrono::NaiveDate |
chrono |
calendar date |
Time |
chrono::NaiveTime |
chrono |
time of day |
DateTime |
chrono::NaiveDateTime |
chrono |
date and local time |
Instant |
chrono::DateTime<chrono::Utc> |
chrono |
UTC time point |
BigInteger |
num_bigint::BigInt |
big-integer |
arbitrary-precision integer |
BigDecimal |
bigdecimal::BigDecimal |
big-decimal |
arbitrary-precision decimal |
Duration |
std::time::Duration |
— | elapsed time |
Url |
url::Url |
url |
parsed URL |
StringMap |
HashMap<String, String> |
— | string-key/string-value properties |
Json |
serde_json::Value |
json |
arbitrary JSON structure |
The 25 variants above are the complete current DataType enum. A feature-gated
variant can still appear in an Unset(DataType) declaration, but a concrete
value of that type requires the corresponding feature in the build that stores
or reads it.
What it provides
ValueandMultiValueshave typed constructors, typed getters, generic mutation, borrowed reads, and explicit unset state.ValueContainer::ScalarandValueContainer::Collectionpreserve shape; a one-item collection remains a collection.get_or/to_orand collection variants make fallback behavior explicit: unset values can use defaults, while type mismatches and ordinary conversion failures are still reported.NamedValueandNamedMultiValuesattach a key to a runtime value without changing the value's type semantics.ValueWireV1provides a versioned, type-preserving JSON representation. Use it when the receiver must reconstruct the exactDataTypeand shape.- Natural JSON helpers produce ordinary
null, scalar, object, and array values when runtime type tags are not wanted.
The crate does not provide a complete configuration store, schema registry,
file format, or distributed cache. It provides the typed value layer those
systems can build on. Its Eq/Hash implementations are suitable for
in-memory Rust collections, not persistent fingerprints or distributed-cache
keys.
Built on Value
Two sibling crates use this value model for key-value containers:
rs-configprovides typed configuration properties, configuration-file and environment-oriented access, and policy-controlled reads. Use it when the key-value data describes application configuration.rs-metadataprovides typed metadata/property storage and filtering. Use it when values describe resources, records, or searchable application metadata.
Wire V1 or Natural JSON?
Choose Wire V1 when a receiver must distinguish Int32(42) from String("42"),
preserve scalar versus collection shape, or retain Unset(DataType). A typical
document is:
Choose to_json_value() when the boundary is ordinary application JSON and
the receiver only needs JSON semantics. Wire V1 is closed and versioned;
Natural JSON intentionally omits runtime type tags. The user guide contains the
full Wire workflow, borrowed payload examples, feature compatibility rules,
and resource-limit handling.
For example, Natural JSON produces these exact JSON strings:
use HashMap;
use DataType;
use ;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
The scalar number becomes 42, the string keeps its JSON quotes, an unset
value becomes null, a concrete collection becomes an array, and string-map
keys are emitted in dictionary order.
Learn more
Testing
# Run tests with the default feature set
# Run tests with all declared features
# Project CI checks
# Check code coverage
License
Copyright (c) 2025 - 2026. Haixing Hu. All rights reserved.
Licensed under the Apache License, Version 2.0. See LICENSE for the full license text.
Contributing
Contributions are welcome. Please follow the Rust API guidelines, keep public
API documentation and tests current, and run ./align-ci.sh to format code and
./ci-check.sh to satisfy CI requirements before submitting a pull request.
Author
Haixing Hu - Qubit Co. Ltd.
Repository: https://github.com/qubit-ltd/rs-value