Skip to main content
Requires feature = "derive". See the derives overview for the shared #[duck(...)] attributes and rename_all casings.
These two derives move whole structs across the row boundary: FromRow reads a query row into a struct, ToRow writes a struct out as a row or as statement parameters.

#[derive(FromRow)] — read a struct from a row

Generates better_duck_core::FromRow: each named field is read from the row by column name via FromDuckValue. ResultSet::to_structs::<T>() collects a whole result into Vec<T>.
  • Named-field structs only — tuple structs and enums are rejected.
  • Container attributes: #[duck(rename_all = "…")], #[duck(crate = ::path)].
  • Field attribute: #[duck(rename = "column")].
  • A NULL read into a non-Option field is a DuckDBConversionError; use Option<T> for nullable columns.

#[derive(ToRow)] — write a struct as a row / params

Generates better_duck_core::AppendAble, appending or binding each field in declaration order — one bulk-appender row, or consecutive $1, $2, … statement parameters.
ToRow binds positionally, in declaration order — there is no column-name mapping, so only the container #[duck(crate = ::path)] option is honored (no rename / rename_all). Order your struct fields to match the target columns / parameters.

See also