Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
cxx-dlang — safe FFI between Rust and D
Bidirectional FFI between Rust and D (LDC2) using cxx.rs and the C++ ABI as interchange — no extern "C" glue, no bindgen, no unsafe pointer casts on the Rust side.
[]
= "1.0"
[]
= "1.0"
*Requires: LDC2 ≥ 1.40, Rust edition 2024 (rustc 1.85+)
How it works
Define the FFI boundary once in a #[cxx::bridge] module. cxx.rs generates
C++ headers; LDC2's extern(C++, "ns") picks them up directly — no manual
.di files, no pragma(mangle) for common cases.
// src/ffi.rs
// d/cxx_d.d
extern(C++, "cxx_d") nothrow
Quick start
# Hello, D! (count=1)
# d_double(21) = 42
# callback result: [hello]
# d_str_len("roundtrip") = 9
# d_make_handle is_null: false
# rust handle: rust-handle
# Greeting { name: "alice", count: 6 }
# Greeting::sizeof == 32
# test result: ok. 10 passed
Set LDC2_PATH=/path/to/ldc2 if ldc2 is not on PATH.
Type mapping
| Rust | C++ (cxx) | D |
|---|---|---|
bool, i32, f64 |
bool, int32_t, double |
bool, int, double |
&str |
rust::Str (16B) |
extern(C++,"rust","cxxbridge1") extern(C++,class) struct Str |
String |
rust::String (24B) |
extern(C++,"rust","cxxbridge1") extern(C++,class) struct String |
Box<T> opaque |
rust::Box<T> |
extern(C++,"cxx_d") extern(C++,class) struct T |
UniquePtr<T> |
std::unique_ptr<T> |
hand-declared value struct unique_ptr(T) + empty dtor |
fn(A) -> R |
rust::Fn<R(A)> |
extern(C++) struct Fn(R,A) + pragma(mangle) for sret |
struct Greeting |
POD struct | extern(C++,"cxx_d") struct Greeting |
rust::Str/String/Fn/Vec live in inline namespace cxxbridge1 — D must use
extern(C++, "rust", "cxxbridge1") to match the linker's mangled names.
Safety
Panic / exception boundary — every Rust function exposed to D is wrapped
with cxx::prevent_unwind; every D function callable from Rust is nothrow.
Violating either is UB.
GC safety — D's druntime scans the stack and heap for pointers. Never
store a Rust Box<T> or Arc<T> in a D class field; druntime will trace
and corrupt Rust memory. Keep opaque Rust handles in a Rust-side registry and
give D an integer key.
Allocator discipline — D handles returned through UniquePtr<T> must be
allocated with __cpp_new_nothrow (from core.stdcpp.new_), not D's GC
new. std::unique_ptr's default deleter calls operator delete; GC memory
is not compatible with that.
LDC2 safety flags — all D compilation units use:
--preview=safer,dip1000,nosharedaccess,fixImmutableConv,systemVariables
Known limitations
- LDC2 only (DMD and GDC not supported)
rust::Fn<R(A)>requirespragma(mangle)— D TMP encodes the template arg as a packJ..Ebut cxx uses a function typeF..E- Async, D class polymorphism are out of scope
References
License
MIT OR Apache-2.0