
Learn how to create a PyO3 function that takes a reference to an enum in Rust. This guide walks you through creating Rust enums, using PyO3, and calling them from Python.
When you’re using PyO3 to create Python bindings for Rust, it’s often necessary to interact with Rust types like enums. PyO3 makes this process a lot easier, allowing you to seamlessly call Rust functions from Python and pass complex types like enums.
In this guide, we’ll go over how to define an enum in Rust, expose it to Python using PyO3, and create a function that accepts a reference to that enum from Python. Whether you’re building a Python extension or just experimenting with Rust and Python integration, this step-by-step explanation will help you get it working.
Create a PyO3 Function that Takes a Reference to an Enum
In Rust, enums are types that can be one of several variants. Enums are a powerful feature of Rust and allow you to define a type that could have multiple possible values. Here’s an example of a simple enum in Rust:
enum Direction {
Up,
Down,
Left,
Right,
}
In this case, the Direction enum can be any one of the four variants: Up, Down, Left, or Right.
Step 1: Set Up PyO3 in Your Project
First, you need to set up a Rust project that will compile into a Python extension using PyO3. If you haven’t done so already, follow these steps:
Install Rust (if you don’t have it installed already). You can do so by visiting https://www.rust-lang.org/.
Create a new Rust project:
cargo new --lib my_rust_extension
cd my_rust_extension
Add PyO3 to your Cargo.toml: Open the Cargo.toml file in your project and add PyO3 under
[dependencies]
pyo3 = { version = "0.17", features = ["extension-module"] }
Create a setup.py for building the extension: You’ll need to write a setup.py file to build the Rust project as a Python extension. Here’s a basic example:
from setuptools import setup
from setuptools_rust import RustExtension
setup(
name="my_rust_extension",
version="0.1",
rust_extensions=[RustExtension("my_rust_extension.my_rust_extension", "Cargo.toml")],
packages=["my_rust_extension"],
zip_safe=False,
)
Install the setuptools-rust package:
pip install setuptools-rust
Step 2: Define the Enum in Rust
Now, let’s define a simple enum in Rust and expose it to Python.
Create a new file, src/lib.rs, and define an enum like so:
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
#[derive(Debug)]
enum Direction {
Up,
Down,
Left,
Right,
}
#[pyfunction]
fn move_player(direction: &Direction) -> String {
match direction {
Direction::Up => "Moving up".to_string(),
Direction::Down => "Moving down".to_string(),
Direction::Left => "Moving left".to_string(),
Direction::Right => "Moving right".to_string(),
}
}
#[pymodule]
fn my_rust_extension(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(move_player, m)?)?;
Ok(())
}
Explanation:
- Define the Enum: We define an enum
Directionwith four variants:Up,Down,Left, andRight. - Write the
move_playerfunction: This function takes a reference to theDirectionenum and returns a string based on the value of the enum. The function matches on the enum and responds with a corresponding string. - Expose it to Python: We use
#[pyfunction]to exposemove_playeras a Python function and#[pymodule]to make the function available in the Python module.
Step 3: Build the Extension
Next, you need to build your Rust project as a Python extension. This step will compile the Rust code into a shared object (.so, .pyd, or .dll depending on your OS).
Run the following in the project directory:
maturin develop
This will build the Rust extension and make it available in your Python environment.
Step 4: Use the Enum and Function in Python
After successfully building the extension, you can now use it in Python. Here’s how to call the Rust function that takes a reference to the enum in Python:
import my_rust_extension
# Create instances of the enum using the class exposed by PyO3
up = my_rust_extension.Direction.Up
down = my_rust_extension.Direction.Down
# Call the Rust function with a reference to the enum
print(my_rust_extension.move_player(up)) # Output: Moving up
print(my_rust_extension.move_player(down)) # Output: Moving down
Explanation:
- Access the Enum: The
Directionenum is now accessible in Python as a class, and we can create instances of the enum by referencing the variants likemy_rust_extension.Direction.Up. - Call the Function: We call the
move_playerfunction, passing a reference to the enum value (up,down, etc.).
Step 5: Handle Enums with More Complexity
If your enum has more complex data (like associated values), you can use PyO3’s #[pyclass] to expose more complex Rust types and work with them seamlessly in Python. However, the basic pattern shown here will help you get started with Rust enums and PyO3.
Conclusion
In this guide, you learned how to define a PyO3 function that takes a reference to an enum in Rust. You now have the foundation to work with Rust enums in Python via PyO3, making your code more efficient, especially when dealing with complex types. Whether you’re building high-performance applications or experimenting with Rust and Python, this approach will be useful for passing enum references between the two languages.
Happy coding, and enjoy exploring the power of Rust and Python together!







