Python is one of the most beloved and powerful coding languages out there. With the adoption of Python for data science and data engineering tasks, the number of Python developers is growing at a huge pace. But Python sometimes is slow, so how can we find a solution for applications that need to fix a Python bottleneck in performance? Rust to the rescue!
Rust is a statically-typed programming language designed for performance and safety, especially safe concurrency and memory management, solving problems that C/C++ developers have been struggling with for a long time.
In this snippet, you will learn how to set up and run Rust from inside your Python code so that whenever you need better performance, you can connect your Rust library inside your script.
Python Virtual Environment Setup on Debian-Based Systems
Let’s start by setting up our environment. First, make sure you’re running Python 3.7 and up. In the terminal, install virtualenv:
sudo apt install virtualenv
And then install virtualenvwrapper:
pip install virtualenvwrapper
Add three lines to your shell startup file (.bashrc, .zsh, etc.):
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/Devel
source ~/.local/bin/virtualenvwrapper.sh
This configuration is important to make sure you’re using a special sandbox for your project, avoiding messing around with your system’s Python libraries.
Installing Rust
You need Rust 1.48 and up. You can install Rust running a single line of code:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
After the installation, add the binaries to your shell startup file (.bashrc, .zsh, etc.):
export PATH="$HOME/.cargo/bin:$PATH"
Using Rust from Python
To quickly set up our code, we will use two libraries: PyO3 and maturin. First, we need to create a new directory and virtual environment for our code, then install maturin via pip:
mkdir rust_from_python
cd rust_from_python
mkvirtualenv rustpi
pip install maturin
Still inside the rust_from_python directory, initiate maturin to generate your Rust package source and choose Pyo3 bindings:
maturin init
Now you can write your Rust code to be used inside Python later on!
Go to src/lib.rs. It will look like:
use pyo3::prelude::*;
/// Formats the sum of two numbers as string.
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
}
/// A Python module implemented in Rust.
#[pymodule]
fn rust_from_python(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
Ok(())
}
Let’s write a function for counting pairs of repeated chars. We will use a method proposed by Red Hat’s engineer Josh Stone. Remove sum_as_string and add the function presented below. Note that you also need to update the method name inside the rust_from_python function:
fn count_doubles_using_bytes(val: &str) -> PyResult<u64> {
let mut total = 0u64;
let mut chars = val.bytes();
if let Some(mut c1) = chars.next() {
for c2 in chars {
if c1 == c2 {
total += 1;
}
c1 = c2;
}
}
Ok(total)
}
Your entire code will look like this:
use pyo3::prelude::*;
/// Formats the sum of two numbers as string.
#[pyfunction]
fn count_doubles_using_bytes(val: &str) -> PyResult<u64> {
let mut total = 0u64;
let mut chars = val.bytes();
if let Some(mut c1) = chars.next() {
for c2 in chars {
if c1 == c2 {
total += 1;
}
c1 = c2;
}
}
Ok(total)
}
/// A Python module implemented in Rust.
#[pymodule]
fn rust_from_python(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(count_doubles_using_bytes, m)?)?;
Ok(())
}
Now save and run maturin develop inside your module directory. This will built the package and install it inside the virtual environment you’re currently on. Now everything is ready to be used from Python!
import rust_from_python
rust_from_python.count_doubles_using_bytes("RRusTT")
The above code will return 2 as a result.
Conclusion
Rust is a great language to use when Python’s performance is not good enough since Rust can be twenty times faster than Python sometimes. With these techniques, you can run Rust from Python whenever you want and still keep Python’s simplicity in the rest of your code. You can even create your own Rust libraries and provide them for your Python team as a black box for time-consuming tasks!
Author
Rodrigo Favarete
Rodrigo Favarete is a Senior Software Engineer at Avenue Code. He holds a bachelor's degree in Computer Engineering. An indie GameDev, he is also passionate about open-source software.