rust-scripting

lpmwfx, Denmark, EU

20.03.2026

Rust as a Scripting Language: Replacing Python in System and Dev Workflows

Python has long been the go-to language for scripting — from system automation to build pipelines and developer tooling. It’s expressive, widely available, and “good enough.” But as projects grow and performance or correctness starts to matter, Python’s limitations become friction. Rust offers a compelling alternative, and increasingly, a practical one.

This article explores two modes of using Rust for scripting: native Rust binaries and rust-script, and why you might reach for one or both in your day-to-day development work.


The Problem with Python for Scripting

Python scripts are often temporary in spirit but permanent in practice. A quick automation script becomes load-bearing infrastructure. Problems that surface along the way include:

Rust solves all of these, with a tradeoff: it requires more upfront ceremony. Whether that tradeoff is worth it depends on the use case — and that’s exactly where the two Rust scripting modes shine in different situations.


Mode 1: Native Rust — When Speed and Correctness Are Non-Negotiable

A native Rust binary is simply a compiled Rust project used as a script replacement. You write a main.rs, compile it, and run it. There’s nothing exotic here — it’s standard Rust.

Why it works so well for scripting

Startup time is near-zero. A native Rust binary starts in milliseconds. Compare this to Python, which must spin up an interpreter, import modules, and resolve dependencies before executing a single line of logic. For scripts that run hundreds of times a day — in CI pipelines, Git hooks, or watch processes — this adds up.

The compiler is your co-pilot. Rust’s type system and borrow checker catch entire classes of bugs at compile time. Your “script” won’t crash halfway through deleting files because you forgot to handle a None case.

Cross-compilation is straightforward. You can compile a script once and distribute a single static binary that runs anywhere — no Python installation required, no pip install, no virtualenv. This is invaluable for developer tooling shared across a team.

Performance is unconditional. Native Rust is among the fastest execution environments available. If your script processes large files, spawns many subprocesses, or does any non-trivial computation, Rust will outperform Python by orders of magnitude.

A practical example

A script that watches a directory, processes incoming files, and dispatches build jobs — written in Python with watchdog and subprocess — is a common pattern. In Rust, the same tool using notify and std::process::Command compiles to a single binary, handles errors explicitly, and runs with near-zero overhead.

use std::process::Command;

fn main() {
    let output = Command::new("git")
        .args(["log", "--oneline", "-10"])
        .output()
        .expect("Failed to execute git");

    println!("{}", String::from_utf8_lossy(&output.stdout));
}

Even this trivial example illustrates the point: error handling is mandatory, the types are clear, and the result is a binary you can ship.

When to use native Rust


Mode 2: rust-script — The Python-like Experience, in Rust

rust-script is a tool that lets you run .rs files directly, like you would a Python script, without a Cargo.toml or a project directory. It handles compilation transparently and caches the result.

rust-script my_tool.rs

You can also declare dependencies inline, directly in the source file:

#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! reqwest = { version = "0.11", features = ["blocking"] }
//! serde_json = "1"
//! ```

fn main() {
    let body = reqwest::blocking::get("https://httpbin.org/get")
        .unwrap()
        .text()
        .unwrap();
    println!("{}", body);
}

This is the Python scripting experience — a single file, run directly — with Rust underneath.

Current status: alpha, but production-worthy

rust-script is technically still in alpha, and its API can change. That said, it is remarkably stable for day-to-day use. The caching mechanism means that subsequent runs of an unchanged script are nearly instant. The inline dependency declaration is expressive and unambiguous. And the full power of the Rust ecosystem — serde, tokio, clap, reqwest, and thousands of other crates — is available from a single file.

The alpha status matters mainly for: automated tooling that depends on specific CLI flags, scripts used in production systems where you need guaranteed stability, and environments where you can’t control the version of rust-script installed. For everything else — local development, personal tooling, team scripts — it works exceptionally well.

The shebang pattern

rust-script supports Unix shebangs, which means you can make a .rs file directly executable:

#!/usr/bin/env rust-script

fn main() {
    println!("Hello from a Rust script");
}
chmod +x my_script.rs
./my_script.rs

This brings the ergonomics as close to Python as possible. The file extension is the only visible difference.

When to use rust-script


A Practical Workflow: From Script to Binary

One of the most natural patterns when working with rust-script day-to-day is letting the tool’s lifecycle determine its form. The workflow looks like this:

A new need arises — parsing some output, automating a repetitive task, gluing two tools together. You reach for rust-script and write a single .rs file. It’s quick, self-contained, and gets the job done. You run it a few times, tweak it, maybe add a dependency inline.

Over time, some of these scripts prove themselves. They stop being “that thing I wrote once” and become tools you rely on — canonical parts of your project or system. At that point, the script graduates: you compile it to a native binary and install it on your system.

# Compile and install directly from a rust-script file
rustc my_tool.rs -o ~/.local/bin/my_tool

# Or promote it to a proper Cargo project first
cargo new my_tool
# move the logic across, then:
cargo install --path .

The promotion requires almost no rewriting. The Rust code you wrote for rust-script is valid Rust — you’re mostly just adding the project scaffolding around it. The inline [dependencies] block maps directly to Cargo.toml. The logic moves over unchanged.

What this gives you is a natural filter: rust-script handles the exploratory, volatile phase where scripts might be rewritten or discarded. Native binaries handle the stable, load-bearing tools that earn their place in $PATH. You don’t have to decide upfront which category a script belongs to — you let usage tell you.


Choosing Between the Two

Concern Native Rust rust-script
Startup time Near-zero First run: seconds. Cached: fast
Distribution Single binary, no runtime needed Requires rust-script installed
Dependencies Cargo.toml Inline in the source file
Project overhead Full Cargo project Single .rs file
Stability Fully stable Alpha — but usable
Best for Long-lived tools, CI, performance Quick scripts, exploration, sharing

The practical workflow is straightforward: start with rust-script when you need something fast, and graduate to a native Cargo project when the script grows up. The code itself rarely needs to change — you’re mostly just adding the project scaffolding around it.


The Ecosystem Advantage

One reason Python dominated scripting is its library ecosystem. Rust’s ecosystem, particularly for systems and developer tooling, is now extremely strong. Libraries like:

…mean that the “Python has a library for everything” advantage has largely closed for the use cases where scripting lives.


Conclusion

Python isn’t going away, and there are contexts — data science, rapid prototyping with ML frameworks, environments where Rust isn’t installed — where it remains the right choice. But for system scripting, developer tooling, and automation that lives inside software projects, Rust is no longer a stretch. It’s a practical, increasingly ergonomic choice.

Native Rust gives you maximum performance and correctness with zero runtime dependencies. rust-script gives you Python-like immediacy with Rust’s type safety and ecosystem underneath. Together, they cover the full scripting spectrum.

The next time you reach for Python to write a build tool or system script, consider whether a .rs file might serve you better. The compiler’s feedback loop is faster than you remember, and the resulting binary will outlast the script it replaced.


Read also: HTML · PDF · Codeberg