Rust -> Python bindings
This guide explains how to add new Python bindings for Rust functions in the garaga_rs
crate.
Overview
The Python bindings are implemented using PyO3, which provides a safe and ergonomic way to create Python extensions in Rust. The bindings are located in the tools/garaga_rs/src/python_bindings
directory.
Adding a new binding
Create a new file in
tools/garaga_rs/src/python_bindings/
for your bindingAdd the module to
mod.rs
Implement your binding function
Register the function in the
garaga_rs
module
Type Conversions
Common Type Conversions
Python Int -> Rust BigUint
let py_int: &Bound<'_, PyInt> = ...;
let biguint: BigUint = py_int.extract()?;
Rust BigUint -> Python Int
let biguint: BigUint = ...;
Ok(biguint.into_pyobject(py)?.into())
Python List -> Rust Vec
let py_list: &Bound<'_, PyList> = ...;
let values: Vec<BigUint> = py_list
.into_iter()
.map(|x| x.extract())
.collect::<Result<Vec<BigUint>, _>>()?;
Python Bytes -> Rust Bytes
let py_bytes: &Bound<'_, PyBytes> = ...;
let bytes = py_bytes.as_bytes();
Field Elements
// Python Int -> Field Element
let fe = element_from_biguint::<YourField>(&biguint);
// Field Element -> Python Int
let biguint = element_to_biguint(&fe);
Error Handling
Converting Rust Errors to Python
// For custom errors
.map_err(PyErr::new::<pyo3::exceptions::PyValueError, _>)?;
// For simple error propagation
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?
Enum Conversion
let value = YourEnum::try_from(py_value)
.map_err(PyErr::new::<pyo3::exceptions::PyValueError, _>)?;
Common Patterns
Function Declaration
#[pyfunction]
#[allow(clippy::too_many_arguments)] // If needed for complex functions
pub fn your_function(
py: Python,
// ... parameters ...
) -> PyResult<PyObject> {
// Implementation
}
Returning Results
// For single values
Ok(result.into_pyobject(py)?.into())
// For lists
let py_list = PyList::new(py, result);
Ok(py_list?.into())
// For tuples
Ok(PyTuple::new(py, &[x, y]).into())
Example Implementation
Here's a complete example showing common patterns:
use pyo3::prelude::*;
use pyo3::types::{PyInt, PyList, PyBytes};
use num_bigint::BigUint;
#[pyfunction]
pub fn example_function(
py: Python,
input_int: &Bound<'_, PyInt>,
input_list: &Bound<'_, PyList>,
input_bytes: &Bound<'_, PyBytes>,
) -> PyResult<PyObject> {
// Convert Python int to BigUint
let value: BigUint = input_int.extract()?;
// Convert Python list to Vec
let values: Vec<BigUint> = input_list
.into_iter()
.map(|x| x.extract())
.collect::<Result<Vec<BigUint>, _>>()?;
// Get bytes
let bytes = input_bytes.as_bytes();
// Do your computation
let result = process_data(&value, &values, bytes)
.map_err(PyErr::new::<pyo3::exceptions::PyValueError, _>)?;
// Return as Python list
let py_list = PyList::new(py, result);
Ok(py_list?.into())
}
Registering Your Function
Add your function to the garaga_rs
module in mod.rs
:
#[pymodule]
fn garaga_rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(your_function, m)?)?;
Ok(())
}
Using the Bindings
After implementing and registering your binding, you can use it in Python like this:
from garaga import garaga_rs
# For simple integer operations
result = garaga_rs.example_function(
input_int=42,
input_list=[1, 2, 3],
input_bytes=b"some bytes"
)
Testing
Add Python tests in
tests/hydra/
Test both successful and error cases
Test edge cases (empty lists, zero values)
Common Pitfalls
Always use
PyResult
for error handlingRemember to propagate errors with
?
Handle edge cases (e.g., zero values, maximum values)
Be careful with memory management for large data structures
Available Utilities
The following utilities are available in the codebase:
element_from_biguint
: Convert BigUint to Field Elementelement_to_biguint
: Convert Field Element to BigUintbiguint_to_u256
: Convert BigUint to u256
Building and Testing
Build/Update the Rust extension:
maturin develop --release --features python
Run tests:
pytest -xs
Best Practices
Keep bindings simple and focused
Use appropriate error handling
Test edge cases thoroughly
Follow existing patterns in the codebase
Last updated
Was this helpful?