// the find
PyO3/pyo3
Rust bindings for the Python interpreter
PyO3 is the standard way to write Python extension modules in Rust, or embed a Python interpreter inside a Rust binary. It handles the messy FFI layer between CPython's C API and Rust's type system, giving you derive macros and proc macros that turn Rust structs and functions into importable Python objects. If you're reaching for C extensions for performance, PyO3 + maturin is now the obvious default.
The macro ergonomics are genuinely good — `#[pyclass]`, `#[pyfunction]`, and `#[pymodule]` cover 90% of use cases with almost no boilerplate. Free-threaded Python 3.13 (no-GIL) support landed and is actively maintained, which most competing approaches don't have yet. The maturin integration makes publishing wheels to PyPI a one-command operation, cross-compilation included. The project has real production validation: pydantic-core, tiktoken, polars, and cryptography all use it, so the edge cases are actually being exercised.
The GIL management model (`Python::attach`, `allow_threads`) is conceptually simple but easy to misuse in async contexts — deadlocking by holding a `Python<'_>` token across an await point is a beginner trap with no compile-time guard. Calling Python from Rust (the embedding direction) still requires linking against a specific Python version's shared library at build time, which makes portable binaries painful. Error messages from proc macro failures are often cryptic and bounce you between Rust compiler output and PyO3's own diagnostics. Python 3.8 support is still listed but CPython itself EOL'd it in October 2024, which creates ambiguity about how much longer that target will actually compile cleanly.