Skip to main content
DuckDB has a rich type system — every integer width, exact DECIMAL, UUID, BIT, BIGNUM, temporals, and the composite LIST / ARRAY / STRUCT / MAP / UNION / ENUM types. better-duck covers all of it behind one Rust enum, DuckValue. This page is the complete mapping, how NULL is represented, and how a few feature flags change which Rust type you read. Values arrive as better_duck_core::types::value::DuckValue. Bind values implement AppendAble (see parameters); the two are the read and write sides of the same type coverage.

The complete DuckValue mapping

DuckValue is #[non_exhaustive] — always include a _ arm when you match on it, so your code keeps compiling as new variants are added. It is Clone + PartialEq + Eq + Hash (floats canonicalized, maps hashed order-independently), so a DuckValue works as a HashMap key.
A column type that read-support does not yet decode surfaces as a DuckDBConversionError rather than panicking. As of 0.1.0-beta.8 the DuckDB C API exposes no value accessor for GEOMETRY, VARIANT, ANY, or INTEGER_LITERAL, so those are not yet readable as typed values — but GEOMETRY physically arrives as its WKB Blob, which you can decode with a geometry library on the Rust side or convert in SQL.
Two beta write-path limitations to know. Multi-arm UNION writes currently build a single-member union (reads are unaffected), and a DECIMAL’s declared width/scale is read into the value but not yet carried back on write. See Versioning & compatibility.

NULL and validity

NULL is first-class, not a sentinel:
  • A missing value reads back as DuckValue::Null.
  • In columnar access a column carries a validity bitmask — packed u64 words where bit r % 64 of word r / 64 set means row r is valid. DuckDB elides the mask entirely for a column with no NULLs, so validity() returns None in that case.
  • TRY_CAST and Option<T> parameters / returns propagate NULL explicitly, so you can round-trip missing data without magic values.
In columnar reads, VectorRef::as_slice does not mask NULL rows — the slice contains whatever bytes back the NULL cell. Pair as_slice with validity() / is_null(row) whenever NULLs are possible. See columnar access.

Feature flags that change the read type

Two decisions change which Rust type a temporal / numeric column decodes to. They are read-type choices — the SQL and the DuckValue variant are unchanged, only the inner Rust type differs.
chrono and the native date/time path are mutually exclusive for the read type — only one set of DuckValue temporal conversions compiles at a time. The separate time feature is an independent bind/append path (time-crate Date, Time, OffsetDateTime, …) that coexists with either read type. See feature flags.

Inspecting types without reading values

Two APIs let you learn a column’s real type — including the detail a coarse type id loses — without decoding any rows:
  • TypeInfo (better_duck_core::types::TypeInfo) is the lossless type descriptor: TypeInfo::Decimal { width, scale }, TypeInfo::List(..), TypeInfo::Scalar(id), nested LIST / STRUCT / MAP shapes, and ENUM dictionaries all survive. On a result, read it with column_logical_type(idx) or column_schema(). The coarser column_type(idx) returns just the DuckDB type id.
  • TableDescription (better_duck_core::TableDescription) reads a catalog table’s column names, types, and DEFAULT flags without issuing a query:
See Queries, Results & Values for the full metadata surface.

Mapping Rust types to DuckDB

With the derive feature, map your own types instead of matching DuckValue by hand:
  • #[derive(FromRow)] / #[derive(ToRow)] — read a struct from a row / write a struct as a row or parameters.
  • #[derive(DuckStruct)] — a Rust struct ↔ a DuckDB STRUCT.
  • #[derive(DuckEnum)] — a Rust unit enum ↔ a DuckDB ENUM (by label).
See data derives for the full attribute set.

SQL dialect & statements

Parameter binding and what each statement returns.

Queries, Results & Values

The DuckValue type and result metadata in depth.

Columnar access

Zero-copy column reads and the validity bitmask.

Data derives

Map Rust structs and enums to rows and types.