Python is popular for good reason: it lets us express ideas quickly and has an enormous ecosystem. But when a workload needs to do a lot of computation, Python itself is rarely the whole story. Libraries such as NumPy and pandas move much of their heavy work into native code, often written in C or C++. Since Python's reference implementation is written in C, it is tempting to assume that one of these languages is the natural choice for writing extensions too.
I came to this question through my research in recommender systems, where I focus on whole-page optimization: choosing and arranging several recommendation rows together. I needed an efficient way to build a full-page recommender. I first tried NumPy and TensorFlow, both strong choices for matrix operations, but expressing the algorithm meant thinking in vectors and matrices throughout. Even then, the implementations were not fast enough for what I needed.
So I gave Rust a try. It turned out much faster than both NumPy and TensorFlow, and the implementation was easier to reason about. The useful surprise was that Rust did not have to mean leaving Python behind.
I could write the algorithm as straightforward loops instead of reshaping every step into a vectorized operation.
Rust was designed with a cohesive toolchain from the start. Cargo formats code, builds projects, and runs tests, all through one familiar interface. Python has separate tools for parts of that job, including Conda, pyenv, and Poetry. Newer Rust-built tools such as uv and Ruff bring speed and a more unified feel to Python dependency management and code quality.
The bridge between the two languages is also simpler than I expected. PyO3 lets Rust functions and types appear in Python, while Maturin takes care of building and packaging the extension. Together, they make it practical to keep Python as the comfortable interface and put only the performance-critical parts in Rust.
In the talk, I walk through what makes Rust different, where Python still has advantages, and how to build an extension with PyO3 and Maturin. I also show how the same approach can reach into Polars: a custom Rust plugin can provide an expression that runs within Polars' query engine, avoiding the overhead of calling back into Python for each value.
The slides are available here for following along or revisiting the examples.