There and Back Again
Last updated on 2026-09-25 | Edit this page

Overview
Questions
- What does Rust add to a successful Python project?
- When is a mixed-language project worth the added complexity?
- Where is Rust already present in the Python ecosystem?
Objectives
- Contrast the strengths and costs of Python and Rust.
- Recognize common motivations for placing Rust behind a Python API.
- Form a specific hypothesis before optimizing or migrating code.
Two languages, two jobs
Python optimizes for developer momentum: expressive code, a rich ecosystem, and a fast path from experiment to working software. Rust optimizes for control and confidence: predictable performance, memory safety without a garbage collector, and errors caught before deployment.
Ask where each language creates the most value in the project.
| Python is often strongest at | Rust is often strongest at |
|---|---|
| orchestration and application logic | tight compute-heavy loops |
| exploration and rapid iteration | predictable memory and latency |
| broad scientific and web ecosystems | safe low-level or concurrent work |
| approachable, flexible APIs | standalone native libraries |
Reasons to cross the boundary
Common motivations include a measured performance bottleneck, memory pressure, parallel work constrained by Python’s runtime, reuse of an existing Rust crate, or a need for stronger guarantees in a small critical component.
Crossing the boundary has costs. Native builds complicate packaging, values must be converted between language runtimes, and maintainers need enough Rust knowledge to support the result. A benchmark and an explicit goal give the decision a foundation.
Turn a hunch into a decision
Before writing Rust, record three things. They keep a promising experiment from quietly becoming an open-ended rewrite.
| Decision input | Question to answer | ACORN-shaped example |
|---|---|---|
| Baseline | What happens now on representative data? | Record median and slowest-case time for a realistic batch of DOI values. |
| Target | What improvement would matter to users or operators? | Reduce validation time enough to shorten an actual ingest job, not merely a microbenchmark. |
| Cost budget | What added complexity is acceptable? | Support the required wheels without making every Python maintainer debug Rust. |
Measure through the interface users will call. A Rust function may be fast in isolation while conversion, repeated boundary crossings, or wheel startup dominates the installed package. The experiment may also succeed without a speedup: reusing a trusted crate or making memory use more predictable can be a valid result when that was the stated target.
Our route: acorn-py
Throughout the workshop we will build a representative slice of acorn-py.
Its Python users work with familiar imports such as:
Behind that API, PyO3 exposes validation and schema behavior from the
Rust crates acorn-lib and acorn-schema. This
is a credible incremental boundary: identifier validation is
self-contained, the Python contract is easy to test, and the Rust
implementation already exists. The Python package exposes a selected
subset of the Rust APIs.
The completed project also gives us production questions to examine:
why the distribution is named acorn-py while the import is
acorn, why it uses the CPython stable ABI, and how its
wheels are tested on more than one Python version.
Write the reason before choosing the tool
Think of a Python project you know. Complete this sentence:
Moving ______ to Rust may improve ______, which we will verify by ______.
Then name one reason that component should remain in Python.
A useful answer names a narrow component, a measurable outcome, and a test. For example: “Moving file-format parsing to Rust may reduce import time, which we will verify with a representative benchmark.” Keeping CLI orchestration in Python may preserve iteration speed and ecosystem integrations.
Rust may already be in your environment
Python packages can expose native code while presenting ordinary Python modules to their users. Projects such as Polars, Pydantic Core, Ruff, uv, and Tokenizers demonstrate different ways Rust can support Python-facing tools. They show that a carefully chosen boundary can work well, while each project still needs its own reason to adopt Rust.
- Python and Rust are complementary when each has a clearly defined role.
- Start from a measured need before considering a rewrite.
- The integration boundary and its maintenance cost are part of the design.
