DEV Community

Nilesh Vishwakarma
Nilesh Vishwakarma

Posted on

Rust Is Becoming the Engine Behind Python’s Modern Tooling

Why Ruff, uv, Pydantic, Polars, and a growing class of Python tools are choosing Rust — and why the shift matters.

Rust engine behind Python tooling

Figure 1 — Conceptual illustration: Python remains the developer-facing layer while Rust increasingly powers performance-sensitive tooling underneath.

The most interesting Rust adoption story may not be happening inside Rust applications.

It may be happening inside Python.

A Python developer today might run:

uv sync
ruff check .
ruff format .
ty check
Enter fullscreen mode Exit fullscreen mode

They might validate API data with Pydantic, process analytical workloads with Polars, or tokenize text using Hugging Face Tokenizers or OpenAI's tiktoken.

From the developer's point of view, this is still a Python workflow.

Underneath it, however, something has changed.

Rust is increasingly doing the heavy lifting.

Ruff is written in Rust. uv is written in Rust. ty is written in Rust. Pydantic moved its core validation engine to Rust. Polars built its core query engine in Rust. Hugging Face Tokenizers uses Rust. OpenAI's tiktoken contains Rust code exposed to Python.

This is not evidence that Rust is replacing Python.

It points to something more useful:

Python is remaining the interface developers want, while Rust is becoming part of the infrastructure underneath it.

That distinction explains why this trend matters.


The Python toolchain is changing

A traditional Python development environment might have combined tools such as:

pip
virtualenv
pip-tools

flake8
isort
Black

mypy
Enter fullscreen mode Exit fullscreen mode

That model still works, and those tools remain important.

But a newer stack can look very different:

                Python project
                     │
        ┌────────────┼────────────┐
        │            │            │
        ▼            ▼            ▼
       uv           Ruff          ty
 packages/projects  lint/format  type checking
        │            │            │
        └────────────┼────────────┘
                     │
                    Rust
Enter fullscreen mode Exit fullscreen mode

Ruff combines linting, automatic fixes, import sorting-related functionality, and formatting in one native tool. uv covers package installation, dependency resolution, project management, virtual environments, lock files, tools, scripts, and Python installations. ty brings a Rust implementation into Python type checking and language-server work.

The interesting story is not that all of these tools are written in the same language.

It is that the workloads they perform have similar characteristics: parsing, graph traversal, semantic analysis, caching, dependency resolution, and repeated processing of large codebases.

Those are areas where implementation architecture can materially affect the developer experience.


Ruff showed what a Rust-first Python tool could feel like

Ruff was one of the projects that made many Python developers notice this shift.

A linter looks simple from the outside:

ruff check .
Enter fullscreen mode Exit fullscreen mode

Internally, however, it needs to discover files, parse Python syntax, construct internal representations, evaluate rules, produce diagnostics, and sometimes rewrite source code.

Historically, a repository could run several tools independently:

Python files
    │
    ├──► Flake8
    ├──► isort
    ├──► pyupgrade
    ├──► autoflake
    └──► Black
Enter fullscreen mode Exit fullscreen mode

Ruff approaches much of that work through one implementation.

Python source
      │
      ▼
 ┌───────────┐
 │   Ruff    │
 │           │
 │ Parsing   │
 │ Linting   │
 │ Fixes     │
 │ Formatting│
 └───────────┘
      │
      ▼
Checked source
Enter fullscreen mode Exit fullscreen mode

Astral publishes benchmarks showing large performance advantages for Ruff in the workloads it measures. Those numbers are useful, but they should be read as project benchmarks, not as a guarantee that every repository will see the same improvement.

More importantly, Ruff illustrates a broader engineering lesson.

Rust alone is not the architecture.

The performance story also involves consolidation, shared parsing infrastructure, caching, data structures, and implementation choices.

A badly designed Rust tool can still be slow. A well-designed Python tool can still be fast enough.

The language matters, but it is only one part of the system.

Ruff also has tradeoffs. Its behavior is not identical to every tool it can replace, and it does not reproduce every extension model from older ecosystems such as Flake8's plugin system.

So migration should follow project requirements, not benchmark headlines.


uv moves Rust into the center of Python package management

Package management makes the trend even more visible.

A conventional workflow might involve several separate concepts:

Python installation
       │
       ├── venv / virtualenv
       ├── pip
       ├── pip-tools
       ├── pipx
       └── project-specific tooling
Enter fullscreen mode Exit fullscreen mode

With uv, the workflow can become:

uv init
uv add fastapi
uv run main.py
Enter fullscreen mode Exit fullscreen mode

Or, for pip-style workflows:

uv venv
uv pip install fastapi
Enter fullscreen mode Exit fullscreen mode

The important detail is that uv pip is not simply a thin wrapper that invokes pip underneath. uv implements its own package-management machinery while providing a familiar interface for many pip-style operations.

That means a Rust program is now handling work such as:

             uv
              │
   ┌──────────┼──────────┐
   │          │          │
   ▼          ▼          ▼
Dependency  Python     Virtual
resolution  versions   environments
   │          │          │
   └──────────┼──────────┘
              ▼
            Project
Enter fullscreen mode Exit fullscreen mode

Again, the attraction is not simply that Rust can execute native code.

uv also consolidates responsibilities that Python developers previously handled through several tools.

That can simplify workflows, but it does not mean uv is an exact behavioral clone of pip. Astral documents compatibility differences, which matter for projects depending on unusual packaging behavior or established enterprise build systems.

A faster tool is valuable only if it is also compatible with the workflow you need.


Pydantic shows the deeper architectural shift

Ruff and uv are standalone tools.

Pydantic demonstrates something more significant for application developers.

Python code still looks like this:

from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int
Enter fullscreen mode Exit fullscreen mode

Nothing about that API looks like Rust.

That is exactly the point.

Pydantic V2 moved core validation and serialization work into pydantic-core, implemented in Rust and exposed to Python.

The conceptual architecture looks like this:

Python to Rust native boundary

Figure 2 — Conceptual architecture: Python provides the developer experience while Rust handles selected native workloads through a boundary such as PyO3.

The developer keeps the Python interface:

  • Python classes
  • type annotations
  • framework integration
  • IDE support
  • the wider Python ecosystem

But the implementation of selected performance-sensitive operations lives in Rust.

This is a much more interesting model than simply "rewriting Python in Rust."

It lets a project change the engine without changing the driver's seat.


Polars applies the same idea to data processing

Polars makes the pattern even clearer.

A Python developer can write:

result = (
    pl.scan_parquet("orders.parquet")
    .filter(pl.col("amount") > 100)
    .group_by("country")
    .agg(pl.col("amount").sum())
    .collect()
)
Enter fullscreen mode Exit fullscreen mode

The interface is Python, but the query engine is implemented in Rust.

Polars also has an architecture that differs meaningfully from pandas. Its lazy API can build a query plan, optimize it, and then execute the resulting work using its engine.

Conceptually:

Python expression
       │
       ▼
Logical query
       │
       ▼
Query optimizer
       │
       ▼
Execution plan
       │
       ▼
Rust engine
       │
       ▼
Data / CPU cores
Enter fullscreen mode Exit fullscreen mode

That distinction matters because it prevents an oversimplified conclusion.

Polars is not merely "pandas rewritten in Rust."

The implementation language changed, but so did important parts of the execution model.

Rust contributes to the design. It does not explain the entire design.


AI developers may already be using Rust without noticing

The same pattern appears in AI infrastructure.

Hugging Face Tokenizers uses a Rust implementation underneath its Python interface. OpenAI's tiktoken also includes Rust code exposed to Python.

From Python, usage remains ordinary:

import tiktoken

encoding = tiktoken.get_encoding("o200k_base")
tokens = encoding.encode("Rust underneath Python")
Enter fullscreen mode Exit fullscreen mode

The Rust boundary is almost invisible.

That invisibility is important.

A successful native layer should not force every Python user to become a systems programmer.

If the package is distributed correctly, the user imports a Python module and works with a Python API. The implementation language becomes an internal engineering decision.


PyO3 and maturin made the hybrid model practical

Python has depended on native code for decades.

NumPy, SciPy, cryptographic libraries, database drivers, ML frameworks, and CPython itself have long depended on C, C++, Fortran, or other compiled languages.

So the new idea is not "Python can call native code."

The newer story is that the Rust-to-Python development path has become much more practical.

PyO3 provides Rust bindings for Python and allows Rust functions and types to be exposed as Python modules.

Conceptually:

Python code
    │
    ▼
   PyO3
    │
    ▼
Rust implementation
Enter fullscreen mode Exit fullscreen mode

Then maturin helps build and package Rust-backed Python projects into Python wheels.

That gives maintainers a workflow resembling:

Rust source
    │
    ▼
   PyO3
    │
    ▼
 maturin
    │
    ▼
Python wheel
    │
    ▼
pip / uv install
Enter fullscreen mode Exit fullscreen mode

When a compatible prebuilt wheel exists, the end user often does not need Rust installed at all.

That changes the economics of using Rust inside Python libraries.


The Rust-powered Python ecosystem is broader than one vendor

The trend is easy to associate with Astral because Ruff, uv, and ty are highly visible.

But the broader ecosystem shows that the pattern is not limited to one company or one product category.

Rust-powered projects across the Python ecosystem

Figure 3 — Selected examples of Rust-powered developer tools, performance libraries, and Python/Rust integration infrastructure. This is illustrative, not exhaustive.

A useful way to group the projects is:

Area Examples Rust's role
Developer tooling Ruff, uv, ty Native CLI, parsing, resolution, analysis
Validation Pydantic Core Validation and serialization engine
Data processing Polars Query and execution engine
Serialization orjson Native JSON implementation
NLP / AI Tokenizers, tiktoken Tokenization and text processing
Integration PyO3 Python/Rust bindings
Packaging maturin Building and distributing Rust-backed Python packages

This is not every Rust-backed project in Python, and it should not be presented as a complete inventory.

The important point is the pattern across categories.


Why Rust keeps appearing

There are many fast languages, and C already underpins enormous parts of Python.

Why Rust?

Several characteristics line up well with tooling and infrastructure workloads.

Native execution

Rust compiles to native code.

For workloads dominated by parsing, serialization, graph resolution, static analysis, or query execution, avoiding interpreter overhead can be valuable.

But native code does not automatically mean fast software.

Algorithms, memory access patterns, caching, I/O, data structures, allocation behavior, and architecture can matter as much as the language.

Memory safety without a garbage-collected runtime

Compilers, linters, type checkers, and query engines often create large graphs and trees in memory.

Rust provides low-level control together with compile-time memory-safety guarantees, without requiring a garbage-collected runtime.

That can be attractive for systems that need predictable control over large internal representations.

The tradeoff is complexity. Rust's ownership and lifetime model asks more from the programmer than Python does.

Concurrency and parallelism

Some workloads can be divided across CPU cores.

Rust provides strong tooling for concurrent systems while catching many unsafe memory-access patterns at compile time.

That does not make every algorithm parallel, but it can provide a strong foundation when parallelism fits the workload.

Standalone distribution

Rust-based CLI tools can be distributed as native executables or packaged through wheels for supported platforms.

For end users, that can make startup and installation feel simple even though the implementation underneath is native code.


The wrong conclusion: "rewrite everything in Rust"

This is where language discussions often become unhelpful.

Imagine an API request that spends its time like this:

Request
   │
   ▼
Python logic       2 ms
   │
   ▼
Database query    80 ms
   │
   ▼
External API     200 ms
   │
   ▼
Response
Enter fullscreen mode Exit fullscreen mode

Suppose you rewrite the 2 ms of Python logic and reduce it to 0.5 ms.

The total request barely changes.

The correct question is not:

Can Rust run this function faster?

The useful question is:

Is this code actually the bottleneck that matters?

Rust makes the most sense where the workload justifies the additional implementation complexity.

That is why its adoption is so visible in areas such as:

Parsing
Static analysis
Dependency resolution
Validation
Serialization
Tokenization
Query optimization
Data processing
Enter fullscreen mode Exit fullscreen mode

Those are workloads where CPU cost, memory layout, latency, and repeated execution can become visible to users.

For ordinary application code dominated by network calls, databases, or external services, a rewrite may offer little practical benefit.

Measure first.

Change second.


Python and Rust are becoming complementary

The most useful lesson is not "Rust versus Python."

It is that a system does not need to use one language everywhere.

A practical architecture can look like this:

┌─────────────────────────────────┐
│             Python              │
│                                 │
│  Application logic              │
│  APIs                           │
│  Automation                     │
│  Data workflows                 │
│  Developer-facing interfaces    │
└────────────────┬────────────────┘
                 │
                 │ Native boundary
                 │
┌────────────────▼────────────────┐
│              Rust               │
│                                 │
│  Parsing                        │
│  Validation                     │
│  Dependency resolution          │
│  Static analysis                │
│  Query execution                │
│  Tokenization                   │
│  Performance-sensitive engines  │
└─────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Python stays where its readability, ecosystem, flexibility, and developer experience are valuable.

Rust is introduced where native execution, memory control, or systems-level implementation characteristics provide a measurable benefit.

The boundary between them is what makes the combination powerful.


Should a Python developer learn Rust?

Not simply because Rust is fashionable.

You do not need Rust to use Ruff.

You do not need Rust to use uv.

You do not need Rust to write Pydantic models or use Polars from Python.

That is one of the strengths of the architecture.

But Rust becomes increasingly relevant if you build:

  • developer tools,
  • parsers or compilers,
  • static analyzers,
  • database or query engines,
  • serialization libraries,
  • performance-sensitive Python extensions,
  • infrastructure that spends substantial CPU time in repeatable native workloads.

Then Rust gives you another option:

Keep the Python interface. Replace only the part that actually needs a different implementation model.

That is much more sensible than rewriting an entire application because one component is slow.


Rust is not replacing Python. It is disappearing underneath it.

That may be the most important part of this trend.

A developer runs:

ruff check .
uv sync
ty check
Enter fullscreen mode Exit fullscreen mode

Another writes:

from pydantic import BaseModel
Enter fullscreen mode Exit fullscreen mode

Another writes:

import polars as pl
Enter fullscreen mode Exit fullscreen mode

Another writes:

import tiktoken
Enter fullscreen mode Exit fullscreen mode

They are still Python developers.

They may not think about Rust at all.

And that may be the strongest sign that the model works.

Rust does not need Python developers to stop writing Python.

It only needs to be useful in the layers where Python benefits from a different implementation model underneath.

That is what Ruff, uv, Pydantic Core, Polars, Tokenizers, PyO3, maturin, and similar projects demonstrate.

So the interesting future is probably not Python or Rust.

It is increasingly Python on top, Rust where it matters underneath.

And for modern Python tooling, that combination may be more important than choosing a winner.


References

Primary or official project documentation used to verify the technical claims in this article:

Top comments (0)