Introduction
PyO3 provides Rust bindings for CPython, enabling developers to write Python extension modules entirely in Rust. It eliminates the need for C-level boilerplate by offering safe, ergonomic macros that handle reference counting and type conversions automatically.
What PyO3 Does
- Exposes Rust functions and structs as Python-callable objects via procedural macros
- Handles CPython reference counting and GIL management transparently
- Converts between Rust and Python types with automatic or custom FromPyObject traits
- Supports calling Python code from Rust for embedding scenarios
- Integrates with maturin and setuptools-rust for packaging and distribution
Architecture Overview
PyO3 generates the CPython C-API glue at compile time through Rust procedural macros like #[pyfunction] and #[pyclass]. When you annotate a Rust function, the macro emits the matching PyMethodDef and type slots that CPython expects. The result is a shared library (.so or .pyd) that Python imports like any C extension, but the source is safe Rust with no manual Py_INCREF calls.
Self-Hosting & Configuration
- Add
pyo3to your Cargo.toml with theextension-modulefeature enabled - Use maturin (
pip install maturin) as the build backend for simple workflow - Set
PYO3_PYTHONenv var to target a specific Python interpreter - Enable the
abi3feature for stable ABI builds that work across Python versions - Run
maturin developfor fast edit-compile-test cycles during development
Key Features
- Zero-overhead access to Python C-API with Rust memory safety guarantees
- Procedural macros eliminate manual FFI boilerplate entirely
- Stable ABI (abi3) support produces wheels compatible with Python 3.7+
- Async support allows Rust futures to integrate with Python asyncio
- Comprehensive type conversion between Rust standard types and Python objects
Comparison with Similar Tools
- cffi — Python-side C FFI that does not require Rust or compiled extensions
- Cython — Python-like language compiled to C, less strict typing than Rust
- pybind11 — C++ to Python bindings with similar ergonomics but in C++
- SWIG — multi-language wrapper generator with broader but less idiomatic output
- ctypes — stdlib foreign function interface with no compilation step
FAQ
Q: Do I need to know the CPython C-API to use PyO3? A: No. PyO3 macros abstract the C-API so you write idiomatic Rust. Understanding the GIL helps for performance tuning but is not required to get started.
Q: Can I call Python from Rust with PyO3? A: Yes. PyO3 supports embedding a Python interpreter inside a Rust application, letting you call Python functions, import modules, and manipulate Python objects from Rust.
Q: How does performance compare to C extensions? A: Rust extensions built with PyO3 perform comparably to handwritten C extensions. The macro layer adds no runtime overhead since all glue is resolved at compile time.
Q: Does PyO3 support async Python code? A: Yes. PyO3 can bridge Rust async functions to Python coroutines, allowing integration with asyncio event loops.