Skip to main content

Introduction

GitHub Docs crates.io docs.rs License: MIT zread

Write DuckDB extensions in plain Rust.

duckfn is a framework for building DuckDB extensions on top of DuckDB's C Extension API. A single attribute turns an ordinary Rust function into a DuckDB scalar, aggregate, table or copy function, a SQL macro, a replacement scan, or a type cast.

use duckfn::{duck_error, duck_scalar_function, duckfn_entrypoint, DuckOptionResult};

/// ```sql
/// SELECT double_it(21); -- 42
/// SELECT double_it(NULL); -- NULL
/// SELECT double_it(13); -- error: unlucky input
/// ```
#[duck_scalar_function]
pub fn double_it(v: Option<i64>) -> DuckOptionResult<i64> {
if v == Some(13) {
return Err(duck_error("unlucky input"));
}
Ok(v.map(|x| x * 2))
}

// Generates the symbol DuckDB looks for when loading the extension.
duckfn_entrypoint!("my_ext");

The attribute generates the FFI wrapper, the column readers and writers, and the registration code, so everything above is safe Rust. One attribute is the whole pipeline:

Why duckfn​

No C/C++ glue codeDuckDB C types never appear in your code. You write Option<i64>, Vec<String>, #[derive(DuckStruct)] structs and #[derive(DuckEnum)] enums.
No local DuckDB buildThe extension is compiled against headers only and dispatches through DuckDB's API table at load time.
Safe by defaultNo unsafe fn and no raw pointers in your function bodies. The only unsafe left is in explicit manual registration.
Attribute-driven registrationAnnotating a function is enough; registration items are collected with inventory and applied when DuckDB loads the extension.
Panic-safeA Rust panic inside a function body is caught and reported as a DuckDB error instead of unwinding across the FFI boundary.
Nested types includedVec<T>, IndexMap<K, V>, fixed-size arrays, STRUCTs and any nesting of them map to DuckDB's LIST, MAP, ARRAY and STRUCT.
Fits DuckDB's CIThe repository reuses DuckDB's official multi-platform extension pipeline, so a version tag produces binaries for every supported platform.

Same functions, two ways puts four functions from DuckDB's official template and the quack-rs example next to their raw implementations.

How the pieces fit together​

CrateRole
duckfnRuntime framework: traits, type adapters, value types, registration. Re-exports every macro.
duckfn-macroThe procedural macros behind #[duck_scalar_function], #[derive(DuckStruct)], …
quack-rsThe DuckDB C API bindings duckfn builds on; its builders and value types are part of the public surface.
libduckdb-sysDuckDB headers; with the loadable-extension feature nothing is linked at build time.

Status​

Early / experimental — APIs may change before 1.0.

Where to go next​

  • Create a project — start from the duckfn extension template, or from DuckDB's official Rust one.
  • Installation — dependencies, MSRV, and why no DuckDB build is needed.
  • Quick start — build and load your first extension.
  • Attributes — the full attribute and argument reference.
  • The example extension — a working extension covering every feature.