Writing Python Extensions with Rust
16 Dec 2024Introduction
Sometimes, you need to squeeze more performance out of your Python code, and one great way to do that is to offload some of your CPU-intensive tasks to an extension. Traditionally, you might use a language like C for this. I’ve covered this topic in a previous post.
In today’s post, we’ll use the Rust language to create an extension that can be called from Python. We’ll also explore the reverse: allowing your Rust code to call Python.
Setup
Start by creating a new project. You’ll need to switch to the nightly Rust compiler:
Next, ensure pyo3
is installed with the extension-module
feature enabled. Update your Cargo.toml
file:
Code
The project setup leaves you with a main.rs
file in the src
directory. Rename this to lib.rs
.
Now, let’s write the code for the extension. In the src/lib.rs
file, define the functions you want to expose and the module they will reside in.
First, set up the necessary imports:
Next, define the function to expose:
This function simply returns the string "Hello, world!"
.
The #[pyfunction]
attribute macro exposes Rust functions to Python. The return type PyResult<T>
is an alias for Result<T, PyErr>
, which handles Python function call results.
Finally, define the module and add the function:
The #[pymodule]
attribute macro defines the module. The add_wrapped
method adds the wrapped function to the module.
Building
With the code in place, build the module:
Once built, install it as a Python package using maturin. First, set up a virtual environment and install maturin
:
Now, build and install the module:
The develop
command that we use here builds our extension, and automatically installs the result into our virtual
environment. This makes life easy for us during the development and testing stages.
Testing
After installation, test the module in Python:
Success! You’ve called a Rust extension from Python.
Python from Rust
To call Python from Rust, follow this example from the pyo3 homepage.
Create a new project:
Update Cargo.toml
to include pyo3
with the auto-initialize
feature:
Here is an example src/main.rs
file:
Build and run the project:
You should see output similar to:
Conclusion
Rewriting critical pieces of your Python code in a lower-level language like Rust can significantly improve performance. With pyo3
, the integration between Python and Rust becomes seamless, allowing you to harness the best of both worlds.