DuckResult borrows DuckDB FFI handles, so it is neither Send nor
Clone. DuckResult::materialize() consumes it into a better_duck_core::ResultSet that holds
no FFI handles — it is Send + Sync + Clone, so it can cross threads and outlive the
connection.
The async facade returns a
ResultSet from every query method — materializing is
what makes an owned result safe to hand back out of a spawn_blocking closure.API
All rows as a slice.
Consume into an owned
Vec<DuckRow>.The first row, if any.
Row count helpers.
Affected-row count (DML);
0 for SELECT.Column names in order.
ResultSet also implements IntoIterator (yielding owned DuckRows), so you can for row in set
or set.into_iter().map(...).
Deserializing into structs
With thederive feature and a #[derive(FromRow)] type,
to_structs::<T>() reads the whole result into a Vec<T>:
to_structs::<T: FromRow>() -> Result<Vec<T>, DuckDBConversionError> reads each row by column name;
the first row that fails conversion surfaces its error. See
FromRow / ToRow.
When to materialize
Materialize
Crossing threads, returning results from a function, caching, random access, or using
to_structs. Pays an up-front copy; frees the connection immediately.Stream (DuckResult)
Large results you fold once, or when you want to stop early. Zero extra copies; keeps the
connection borrowed until dropped.
See also
- Iterating results — the streaming
DuckResultAPI. - FromRow / ToRow — the
to_structstarget. - Async — returns
ResultSetfrom every query.