Interop Best Practices
Rules for safe language boundaries, clear error mapping, and explicit memory ownership.
Search across all documentation pages
Rules for safe language boundaries, clear error mapping, and explicit memory ownership.
repr(C). Never expose Rust default layouts.lib_version() for compatibility checks.PyErr, JS Error, C status codes.allow_threads on hot paths.ThreadsafeFunction when needed.cargo deny + host package audit.size_of vs C sizeof in CI.PyO3 for extension modules and ergonomics. ctypes for loading arbitrary cdylib without building as module.
Whenever the consumer is not Rust: C++, Swift, Go, JVM, Unity, etc.
Yes with pyo3-async-runtimes and a running event loop policy documented in README.
Fine for control plane. Avoid for hot loops moving megabytes.
Keep symbol, log warning, remove next major. Never repurpose enum values.
Split into core Rust crate plus thin py, napi, and ffi adapter crates.
WASM for sandboxed plugins; native for max performance and OS API access.
Treat FFI as attack surface. Fuzz C API entrypoints.
Host provides log callback registered at init, or return structured error strings only.
cbindgen for C, napi for .d.ts, Sphinx via pyo3 docstrings for Python.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Reviewed by Chris St. John·Last updated Jul 16, 2026