Skip to content

Recipe for wrapping Rust with PythonΒΆ

Here are some notes on how to wrap a Python package around fast, parallelized Rust code.

Create a mixed Rust-Python project:

maturin new dprs -b pyo3

and then, if you want, rename the directory dprs/, to e.g. DPRS/. In principle, this will become the name of your pip package. Enter this directory, cd DPRS/.

Create a Python virtual environment using uv, activate it, and install whatever Python libraries are going to be needed:

uv venv --python=3.14
source .venv/bin/activate
uv pip install maturin ipython numpy

Set up the minimal elements of a Python package:

mkdir -p python/dprs
touch python/dprs/__init__.py

Add the requisite Rust packages, e.g.,

cargo add rayon rand

Check Cargo.toml to ensure that these crate dependencies have been added (see https://www.maturin.rs/tutorial.html):

[dependencies]
pyo3 = "0.27.0"
rand = "0.10.0"
rayon = "1.11.0"

Implement src/lib.rs. The Python module can follow this naming pattern:

#[pymodule]
mod sim {
    use super::*;
    #[pyfunction]
    fn dp(x: usize, y: usize, n: usize) -> PyResult<String> {
        println!("{x} {y} {n}");
        run(x, y, n);
        Ok("Done".to_string())
    }
}

For this to build correctly, mod pyproject.toml like this:

[tool.maturin]
python-source = "python"
module-name = "dprs.sim"

Add <path>/DPRS/python to VS Code's extra paths for Pylance.

Compile the Rust and build a Python binary:

maturin develop --release

which writes to e.g. <path>/DPRS/.venv/lib/python3.14/site-packages/dprs/dprs.cpython-314-darwin.so.

Build a Python wheel:

maturin build --release

which writes to e.g. <path>/DPRS/target/wheels/dprs-0.1.0-cp314-cp314-macosx_11_0_arm64.whl as well as creating dylib file in 'target/maturin' etc.

Then install the Python wheel into the local venv using uv pip:

uv pip install target/wheels/*.whl

Create a Python demo script, and maybe also a Jupyter notebook to match, e.g.,:

mkdir demos
touch demos/check.py

Put at least the following into demo.py:

from dprs import sim
print(sim)

n_x = 1_000
n_y = n_x
n_iterations = 200

sim.dk(n_x, n_y, n_iterations)

The DPRS folder tree should now look like this, more or less:

folder tree
Figure 1: folder tree