Python Module

Python module generation is powered by unstable Procedural Macros feature, so you need to turn on proc_macro feature:


extern crate pyo3;
# fn main() {}

You need to change your crate-type to cdylib to produce a Python compatible library:

name = "rust2py"
crate-type = ["cdylib"]

pyo3 = { version = "0.2", features = ["extension-module"] }

Now you can write your module, for example


extern crate pyo3;
use pyo3::{py, PyResult, Python, PyModule};

use pyo3::py::modinit as pymodinit;

// add bindings to the generated python module
// N.B: names: "librust2py" must be the name of the `.so` or `.pyd` file
/// This module is implemented in Rust.
fn init_mod(py: Python, m: &PyModule) -> PyResult<()> {

    // pyo3 aware function. All of our python interface could be declared in a separate module.
    // Note that the `#[pyfn()]` annotation automatically converts the arguments from
    // Python objects to Rust values; and the Rust return value back into a Python object.
    #[pyfn(m, "sum_as_string")]
    fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult<String> {
       let out = sum_as_string(a, b);


// logic implemented as a normal rust function
fn sum_as_string(a:i64, b:i64) -> String {
    format!("{}", a + b).to_string()

# fn main() {}

The modinit procedural macro attribute takes care of exporting the initialization function of your module to Python. It takes one argument as the name of your module, it must be the name of the .so or .pyd file.

The Rust doc comments of the module initialization function will be applied automatically as the Python doc string of your module.

import rust2py


Which means that the above Python code will print This module is implemented in Rust..

On macOS, you will need to rename the output from *.dylib to *.so.

On Windows, you will need to rename the output from *.dll to *.pyd.

For integration, You can use setuptools-rust, learn more about it in Distribution.