Even though [[Python]] is infamously slow, the ecosystem has been able to dodge that issue by using different performant languages to write the core of the packages. It is very easy to get started doing this, using [[maturin]] and following the steps presented in [[Accelerate Your Python Code With Rust the Python Exchange January 2025|"Accelerate Your Python Code With Rust | the Python Exchange January 2025" by Don't Use This Code • James Powell]] ## Getting started We can use the tool interface of [[uv]], `uvx`, to make use of [[maturin]]. This tool will create a [[Rust]] crate with all the necessary bindings to be compatible with Python. Start with: ```shell # Initialize the crate with pyo3 bindings uvx maturin new -b pyo3 --src your-crate-name ``` Now, edit the `cargo.toml` to add the `abi3-py38` feature, enabling compatibility from [[Python 3.8]] onwards. ```toml [dependencies] # pyo3 = "0.25.0" <- change this line for: pyo3 = { version = "0.25.0", features = ["abi3-py38"] } ``` And then it's ready to build using: ```shell uvx maturin build # [...] # 📦 Built wheel for abi3 Python ≥ 3.8 to ... your-crate-name-0.1.0-cp38-abi3-macosx_11_0_arm64.whl ``` You can then go ahead and edit the structure to use [[Python src layout|src layout]], moving all the [[Python]] code to `src/` and the [[Rust]] code to `rust/`, which is automatically detected by `maturin` even though [it requires some extra modifications](https://www.maturin.rs/project_layout.html#alternate-python-source-directory-src-layout). It boils down to marking the rust code as protected and marking the functions adequately. Once done, you can set up your `__init__.py` to the recommended by [[maturin]]: ```python from ._your_crate_name import * __doc__ = _your_crate_name.__doc__ if hasattr(_your_crate_name, "__all__"): __all__ = _your_crate_name.__all__ ``` And you are ready to build and test the sample code from a [[Python]] REPL: ```python # $ uv run python # Python 3.12.4 (main, Jul 25 2024, 22:11:22) [Clang 18.1.8 ] on darwin # Type "help", "copyright", "credits" or "license" for more information. >>> import _your_crate_name >>> pelorus._your_crate_name(3, 4) '7' ``` One last change for a quality of life improvement: make `uv` watch for changes in [[Rust]] files as well, in `pyproject.toml`: ```toml [tool.uv] # Rebuild the package when the Rust files change cache-keys = [ { file = "pyproject.toml" }, { file = "rust/Cargo.toml" }, { file = "**/*.rs" } ] # Uncomment to build the Rust code in development mode # config-settings = { build-args = "--profile=dev" } ``` ## Writing [[Rust]] code Once the boilerplate is set, simply: - Add the `#[pyfunction]` macro before the functions that are to be exported to the [[Python]] wheel. - If you need to handle errors, you should return a `PyResult<T>`, defined as `Result<T, PyError>`. - Every time you add a function, you need to register it to a `#[pymodule]` with the `.add_function` method.