Rust and Python

(1660 words, 8 minute read)

Should you use Rust to speed up your Python code? Well, it depends on what sort of work you’re doing.

I’m doing CPU work I know I need Rust

Are you doing CPU work in Python and it’s just to slow? Rust with PyO3 might be a decent option, but let’s not be too hasty. Before you jump to that, you might want to do some profiling with something like austin to see where you’re keeping the CPU busy. You might be surprised to find that a lot of the work is in a copy or deepcopy that you can easily refactor away with a little thinking. You might have one little container object and you’re spending a lot of time in attribute lookup on that container object that you can optimize by fiddling around with __slots__. Maybe you wrote some O^2 code that could be refactored into O(log N) with a little caching, maybe toss a set in there or mess around with functools.cache.

It’s also entirely possible that you can just stand up another Python process and communicate to it. Could you put that CPU work that you’ve already written into another Python process and call it over the network? What if that other process was written in a faster language? What if that other process was on the same machine and you talked to it through a Unix socket? Would the serialization and deserialization tax negate any sort of wins? Did you already replace the stdlib json module with orjson in your project?

Is your CPU work mostly numerical, scientific, or data work that could be done with an existing specialized performance library like SciPy, NumPy, Pandas, or Polars?

None of that helps me I really need a compiled Python module

Why? Why? Why?

If you’ve gotten here you’ve convinced yourself the other options are all bad, but the ability to name a bad thing does not negate the possible existence of a good thing. There might be something out there that already exists that you can use to avoid getting into the territory of writing a compiled Python module. So what even IS it about writing a compiled Python module that helps you?

If you’re in this territory, a compiled Python module has these added benefits:

These are the classic wins of monoliths in the monolith versus microservice religious wars. If you’re already operating under a largely monolithic architecture, these benefits might slot in nicely with your existing project. If you’re already doing microservices, think long and hard about why a compiled Python module is better than just writing a Rust microservice instead.

Of course, these benefits are also true of other compiled-Python environments. Have you ruled out writing your Python module in C or C++? How about compiling Python code into machine code with Cython or Numba?

Here are some benefits of using Rust instead of those other things:

Neat, ok, fine, what’s the catch:

Great, Here I go, gonna rewrite all my business logic in Rust!!!

Wait wait wait, stop, stop, stop. Here’s where discussions can often get off the rails. What is actually your problem with Python? Is it the way the type system works? Do you hate the existence of None, exceptions, and inheritance? Do you not like significant whitespace? Are you made about function coloring? These are, frankly, not great reasons to adopt Rust and PyO3 into an existing codebases. Sure, those things can be legitimate concerns (well, except for the significant whitespace thing, get over it), but those aren’t on their own good justification for rewriting existing business logic.

Can a highly-experienced Rust engineer be productive writing business logic in Rust? Absolutely. The argument that you can’t write business logic in Rust doesn’t really convince me of much; people making this argument generally don’t know how to write decent Rust code. It is certainly possible to write business logic in Rust, and experiencd Rust engineers can be extremely productive doing exactly that. That is not itself justification for writing your business logic in Rust, though, especially when stacked against Python, which is arguably the single best programming language for expressing business logic of all time. Why else would it be the world’s most widely know programming language? It’s not easy to operationalize, it’s not particularly performant, and it’s got terrible safety parameters. Python is great because it’s comparatively easy to learn and can succinctly express business logic. Why write your business logic in something else?

Generally speaking, Rust is a relatively poor choice for expressing business logic compared to other programming languages, because of the realities of the software industry and how hiring and engineering career paths work. Outside of specialized domains, engineers tend to move farther away from business logic as they get more senior. Think about it this way: which of these two stories seems more likely?

I started my career exclusively writing business logic in web apps, and as I’ve gotten more senior, I have written less and less business logic as my career has grown. My guess is this is probably a common story! I imagine a lot of engineers’ careers look like this. That’s not a value judgement about any engineers’ level of inherent skill or intelligence in any way: it is how the software industry as a whole tends to be organized.

Rust has a known steep learning curve and anyone who tells you that Rust is easy to learn is either fooling you, fooling themselves, has never written Rust in production, or is a statistical outlier. In general, learning Rust takes a decent amount of time and effort. Are you going to have all of the people on your Python project learn Rust? Probably not. You’ll probably have a portion doing that work. That portion should be the people on the teams that are charged with building capabilities, not the people on the teams that are charged with building end-user product features. As it turns out, the problem is not that Rust cannot express business logic, but that the population of people whose primary job function is to write business logic are not the population of people for whom the steep learning curve will yield a positive return on invesment.

So are you for this or against this?

It’s got some cool properties and there’s definitely a real reason why the Rust-Python mix is gaining popularity. It can be an extremely powerful combination. At the end of the day, it’s just one technique among many, but not to waffle on this too much: yeah I’m for it, I think it’s rad as hell, I wouldn’t have written this post if I didn’t think that, but you have to judge on a project-by-project and team-by-team basis whether it’s a good fit for what you’re working on. Hopefully this post has given you some clarity on what sort of questions you can ask yourself to help weigh whether or not your project is a good candidate for mixing Python and Rust.