Create a PyO3 Function that Takes a Reference to Enum Rust

Written By: Nathan Kellert

Posted On:

How to Create a PyO3 Function that Takes a Reference to an Enum in Rust

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:

  1. Define the Enum: We define an enum Direction with four variants: Up, Down, Left, and Right.
  2. Write the move_player function: This function takes a reference to the Direction enum and returns a string based on the value of the enum. The function matches on the enum and responds with a corresponding string.
  3. Expose it to Python: We use #[pyfunction] to expose move_player as 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:

  1. Access the Enum: The Direction enum is now accessible in Python as a class, and we can create instances of the enum by referencing the variants like my_rust_extension.Direction.Up.
  2. Call the Function: We call the move_player function, 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!

Photo of author

Nathan Kellert

Nathan Kellert is a skilled coder with a passion for solving complex computer coding and technical issues. He leverages his expertise to create innovative solutions and troubleshoot challenges efficiently.

Leave a Comment