Basic Rust and C++ InterOp
Senior Software Engineer from India. Loves to build software that bring better experiences to people.
Search for a command to run...
Senior Software Engineer from India. Loves to build software that bring better experiences to people.
This series provides practical articles and blog posts on everything related to Python Programming language and Software Development in Python ecosystem.
In the exhilarating realm of software development, one fundamental truth prevails – change is the only constant. As technology hurtles forward, the evolution of design patterns and principles has become the compass guiding developers through the dyna...

Data compression is the process of reducing the amount of data required to represent a given piece of information. This is typically done by removing redundant or unnecessary information from the data, or by using algorithms and techniques that encod...

XML parsing refers to the process of analyzing and extracting data from an XML (Extensible Markup Language) document. XML is a popular format for storing and sharing structured data, such as that used in websites or applications. To parse an XML docu...
Database connection pooling is a technique used to improve the performance of database-driven applications by reusing a set of pre-opened database connections. In a typical database-driven application, the application opens a database connection, exe...
Interoperability between different programming languages is a common requirement in many software projects. In this article, we will explore how to interoperate between the Rust and C++ programming languages, allowing you to use the strengths of both languages in your projects.
Rust is a modern, statically-typed programming language that offers low-level control and concurrency without sacrificing safety and reliability. C++ is a high-performance, low-level programming language that is widely used in systems programming and other performance-critical applications.
To interop between Rust and C++, we will use the rust-cpython crate, which provides safe and idiomatic Rust bindings for the Python C API. This crate allows you to call Rust functions from C++ and vice versa, making it easy to integrate Rust and C++ code in your projects.
To begin, we will add the rust-cpython crate to our Cargo.toml file as a dependency.
[dependencies]
rust-cpython = "0.4.0"
Next, we will import the rust-cpython crate and the Python and PyObject types in our Rust code.
use rust_cpython::{Python, PyObject};
Now, we can define a Rust function that we want to call from C++. This function must take a &Python argument and return a PyObject. In this example, we will define a simple add function that takes two i32 arguments and returns their sum.
fn add(py: &Python, a: i32, b: i32) -> PyObject {
// Return the sum of the two arguments.
(a + b).to_py_object(py)
}
To call this function from C++, we will use the rust_cpython::PyModule class to define a Rust module that exports the add function. We will then use the PyImport_AppendInittab function from the Python C API to register the Rust module and make it available to C++.
rust_cpython::PyModule::new(py, "rust")
.add("add", py_fn!(py, add(a: i32, b: i32)))
.register()
.unwrap();
// Register the Rust module in the Python interpreter.
PyImport_AppendInittab("rust", rust_cpython::init_module);
Now, we can call the add function from C++ using the Python C API. To do this, we will import the Python.h header, which provides declarations for the Python C API. We will then use the PyImport_ImportModule function to import the Rust module, the PyObject_GetAttrString function to get the add function from the module, and the PyObject_CallObject function to call the add function with the desired arguments.
#include <Python.h>
// Import the Rust module.
PyObject* module = PyImport_ImportModule("rust");
// Get the `add` function from the module.
PyObject* func = PyObject_GetAttrString(module, "add");
// Call the `add` function with the desired arguments.
PyObject* result = PyObject_CallObject(func, Py_BuildValue("(ii)", 1, 2));
// Print the result.
printf("%d\n", PyLong_AsLong(result));
This code will print 3 to the standard output, indicating that the add function was called successfully from C++.
In addition to calling Rust functions from C++, you can also call C++ functions from Rust using the rust-cpython crate. To do this, you will need to define the C++ function using the extern "C" keyword, which ensures that the function has a C-compatible signature.
For example, if you have a C++ function called multiply that takes two int arguments and returns their product, you can define the function in a header file as follows:
#ifndef MULTIPLY_H
#define MULTIPLY_H
#ifdef __cplusplus
extern "C" {
#endif
// Define the `multiply` function.
int multiply(int a, int b);
#ifdef __cplusplus
}
#endif
#endif
You can then implement the multiply function in a C++ source file and compile it as a shared library.
#include "multiply.h"
// Implement the `multiply` function.
int multiply(int a, int b) {
return a * b;
}
Once you have compiled the shared library, you can call the multiply function from Rust using the rust-cpython::PyCapsule class. This class allows you to create a Rust wrapper for a C++ function, which you can then call from Rust using the call method.
// Import the `PyCapsule` type from the `rust-cpython` crate.
use rust_cpython::PyCapsule;
// Define a Rust wrapper for the `multiply` function.
struct Multiply {
multiply: PyCapsule,
}
impl Multiply {
fn new(py: &Python) -> Self {
// Load the `multiply` shared library.
let multiply = PyCapsule::new(py, "multiply", "multiply").unwrap();
Self { multiply }
}
// Define a method that calls the `multiply` function.
To call the multiply function from Rust, you can define a method on the Multiply struct that uses the call method of the PyCapsule to call the C++ function. This method takes the arguments for the function as a PyTuple and returns the result as a PyObject.
impl Multiply {
// ...
// Define a method that calls the `multiply` function.
fn multiply(&self, py: &Python, a: i32, b: i32) -> PyObject {
// Call the `multiply` function with the desired arguments.
self.multiply
.call(py, (a, b).to_py_tuple(py))
.unwrap()
}
}
Now, you can use the Multiply struct to call the multiply function from your Rust code.
let multiply = Multiply::new(py);
// Call the `multiply` function and print the result.
println!("{}", multiply.multiply(py, 2, 3).extract::<i32>(py).unwrap());
This code will print 6 to the standard output, indicating that the multiply function was called successfully from Rust.
In summary, the rust-cpython crate provides a convenient and safe way to interop between Rust and C++. By using this crate, you can call Rust functions from C++ and C++ functions from Rust, allowing you to use the strengths of both languages in your projects.