There and Back Again

Last updated on 2026-09-25 | Edit this page

Estimated time: 15 minutes

A round-doored hillside dwelling at sunset opposite a large acorn resting in moss.

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:

PYTHON

from acorn.schema.validate import is_doi

assert is_doi("10.11578/dc.20250604.1")

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.

Challenge

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.

Key Points
  • 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.