Skip to main content
better_duck_core::types::value::DuckValue is the dynamic representation of any DuckDB column value. Reading a row gives you &DuckValues; binding parameters and appending accept them.
DuckValue is #[non_exhaustive] — always include a _ arm when matching, so a value from a future DuckDB type cannot break your build.

Variant families

The full variant list is split across focused pages:

Scalar types

Booleans, every integer width, floats, text, blob, decimal, uuid, bit, bignum.

Temporal types

Dates, times, timestamps, intervals, and ±infinity sentinels.

Composite types

LIST, ARRAY, STRUCT, MAP, UNION, ENUM.
The Null variant represents SQL NULL and is shared by all families.

Traits and equality

DuckValue is Clone + PartialEq + Eq + Hash:
  • Floats are canonicalized for Eq/Hash (so NaN behaves consistently), and Map values hash order-independently — a DuckValue works as a HashMap key.
  • Option<T> converts to Null / Some(..) on both read and write directions.

Constructors and Map helpers

  • DuckValue::text(impl Into<String>) -> DuckValue — build a Text value.
  • On a Map variant: get(key), get_mut(key), contains_key(key), taking any key convertible into DuckValue. On non-Map variants they return None / false.

The conversion traits

Four traits connect Rust types and DuckDB values. You rarely implement them by hand — the derives and built-in impls cover the common cases — but knowing the roles clarifies error messages and generic bounds.
Write direction. Binds to a prepared-statement parameter or appends to an appender row. Implemented for every DuckValue and the primitive Rust types. This is the bound on execute_with and Appender::append.
Read direction. from_duck_value(&DuckValue) -> Result<Self, DuckDBConversionError>. A NULL into a non-Option target is NullValue; a mismatched variant is a conversion error. Option<T> reads Null as None.
The low-level from_duck / to_duck round-trip against a raw FFI value (or, for temporal types, a packed FFI struct). The machinery AppendAble/FromDuckValue build on.
Gives a Rust type (not value) its fixed DuckDB logical type, so Vec<T>, Box<[T]>, and HashMap<K, V> can map to LIST/ARRAY/MAP even when empty. See LogicalType::of::<T>().
DuckValue deliberately does not implement DuckLogicalType: the enum has no single fixed logical type (it depends on the runtime variant). Wrap a Vec<DuckValue> as DuckValue::List(v) and bind it directly instead.

Converting a whole value

  • to_duck() -> Result<duckdb_value, DuckDBConversionError> — build a heap DuckDB value (caller destroys it). Errors for an empty collection whose element type can’t be inferred.
  • logical_type_of(&DuckValue) -> Result<duckdb_logical_type, _> — the value’s logical type.
  • to_owned_value() -> Result<OwnedValue, _> — materialize an OwnedValue for introspection (SQL-NULL check, SQL rendering, STRUCT child access).

See also