quack-rs: DuckDB extensions in Rust
A Rust SDK for building DuckDB loadable extensions — no C or C++ to write.
What is quack-rs?
quack-rs is a Rust SDK for building DuckDB loadable extensions
without writing C++ or CMake. It wraps DuckDB's C Extension API — the C interface DuckDB exposes
to loadable extensions — in safe builders and RAII types, and guards against the FFI
pitfalls documented in the Pitfall Catalog, so you can focus on
extension logic.
DuckDB's community-extensions FAQ still says that "Currently, writing a Rust-based DuckDB
extension requires writing glue code in C++", and that a pure-Rust path is in development
(DuckDB Community Extensions FAQ).
That path now exists in two forms. DuckDB's own extension-template-rs targets the unstable
C API v2 with the experimental duckdb-neo crate. quack-rs targets the stable C API v1,
so one binary loads across DuckDB releases. Your repository holds Rust only: no C++, no
CMake, and no glue layer of your own between your functions and the C API. (Some of the
tools around it still build C: see Build requirements.)
What you can build
| Extension type | quack-rs support |
|---|---|
| Scalar functions | ✅ ScalarFunctionBuilder |
| Overloaded scalars | ✅ ScalarFunctionSetBuilder |
| Aggregate functions | ✅ AggregateFunctionBuilder |
| Overloaded aggregates | ✅ AggregateFunctionSetBuilder (per-overload return types) |
| Table functions | ✅ TableFunctionBuilder (raw) + TypedTableFunctionBuilder<S> (closure-based, typed scan state) |
| Cast / TRY_CAST functions | ✅ CastFunctionBuilder |
| Replacement scans | ✅ ReplacementScanBuilder |
| SQL macros (scalar) | ✅ SqlMacro::scalar |
| SQL macros (table) | ✅ SqlMacro::table |
Copy functions (COPY TO / COPY FROM) | ✅ CopyFunctionBuilder (requires duckdb-1-5) |
Note: Window functions have no counterpart in DuckDB's public C Extension API and cannot be implemented from Rust (or any language) via that API. See Known Limitations.
Why does this exist?
quack-rs was extracted from
duckdb-behavioral, a production DuckDB
community extension. Building that extension revealed the first undocumented pitfalls
in DuckDB's Rust FFI surface — struct layouts, callback contracts, and initialization
sequences that aren't covered anywhere in the DuckDB documentation or libduckdb-sys docs.
The catalogue has grown since, from building and auditing quack-rs itself.
Three of those pitfalls caused extension-breaking bugs that passed 435 unit tests before being caught by end-to-end tests:
- A SEGFAULT on load (wrong entry point sequence)
- 6 of 7 functions silently not registered (undocumented function-set naming rule)
- Wrong aggregate results under parallel plans (combine callback not propagating configuration fields to fresh target states)
quack-rs rules out the first two: entry_point! performs the correct initialization
sequence, and the function-set builders name every member. The third lives in your own
combine callback, so no API can prevent it; AggregateTestHarness::combine lets you test
for it without DuckDB. The full list is in the Pitfall Catalog.
Key features
- No C++ in your repository — no
CMakeLists.txt, no header files, no glue layer of your own - Every function kind the C API can register — scalar, aggregate, table, cast, replacement scan, copy function (
duckdb-1-5) — plus SQL macros - Panic-safe FFI — the entry point and the callbacks quack-rs generates catch panics and report them as SQL errors; registration errors surface via
Result - RAII memory management —
LogicalTypeandFfiState<T>prevent leaks and double-frees - Type-safe builders —
ScalarFunctionBuilder,AggregateFunctionBuilder,TableFunctionBuilder,CastFunctionBuilder,ReplacementScanBuilder - SQL macros — register
CREATE MACROstatements without any FFI callbacks - Testable state —
AggregateTestHarness<T>tests aggregate logic without a live DuckDB - Scaffold generator — generates a complete community extension project (
Cargo.toml,Makefile, CI,description.yml, tests) from one function call - 38 pitfalls documented — every known DuckDB Rust FFI pitfall, with symptom, root cause and fix
Navigation
New to DuckDB extensions? → Start with Quick Start
Adding quack-rs to an existing project? → See Installation
Writing your first function? → See Scalar Functions or Aggregate Functions
Want SQL macros without FFI callbacks? → See SQL Macros
Submitting a community extension? → See Community Extensions
Something broke? → See Pitfall Catalog