To create a Python extension module in Rust that operates on NumPy arrays, you can use PyReadonlyArrayDyn for immutable access and PyArrayDyn for mutable or owned arrays.
Key patterns:
- Immutable access: Use
PyReadonlyArrayDyn<'py, T> to receive arrays from Python. Convert them to ndarray views using .as_array() to perform computations. - Mutable in-place modification: Use
&Bound<'py, PyArrayDyn<T>> and access the underlying mutable array via unsafe { x.as_array_mut() }. - Returning arrays: Use
.into_pyarray(py) on an ndarray type to convert it back into a Python-managed NumPy array.
[lib]
name = "rust_ext"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.29" }
numpy = "0.29"
#[pyo3::pymodule]
mod rust_ext {
use numpy::ndarray::{ArrayD, ArrayViewD, ArrayViewMutD};
use numpy::{IntoPyArray, PyArrayDyn, PyReadonlyArrayDyn, PyArrayMethods};
use pyo3::{pyfunction, PyResult, Python, Bound};
// example using immutable borrows producing a new array
fn axpy(a: f64, x: ArrayViewD<'_, f64>, y: ArrayViewD<'_, f64>) -> ArrayD<f64> {
a * &x + &y
}
// example using a mutable borrow to modify an array in-place
fn mult(a: f64, mut x: ArrayViewMutD<'_, f64>) {
x *= a;
}
// wrapper of `axpy`
#[pyfunction(name = "axpy")]
fn axpy_py<'py>(
py: Python<'py>,
a: f64,
x: PyReadonlyArrayDyn<'py, f64>,
y: PyReadonlyArrayDyn<'py, f64>,
) -> Bound<'py, PyArrayDyn<f64>> {
let x = x.as_array();
let y = y.as_array();
let z = axpy(a, x, y);
z.into_pyarray(py)
}
// wrapper of `mult`
#[pyfunction(name = "mult")]
fn mult_py<'py>(a: f64, x: &Bound<'py, PyArrayDyn<f64>>) {
let x = unsafe { x.as_array_mut() };
mult(a, x);
}
}