A while ago I stumbled upon rust-script, a tool which lets you write single-file Rust programs and execute them as if they were standalone script files.

This comes in very handy when you want to experiment with Rust code, write up executable examples or build small utility programs, but don’t always want to deal with explicitly setting up Cargo projects for every little script.

Using rust-script

Let’s have a look at how this works:

First, we install the rust-script binary: cargo install rust-script.

Next, we write a sample script and make it executable (chmod +x example.rs):

example.rs:

#!/usr/bin/env rust-script

fn main() {
    println!("Hello, world!");
}

And finally, we execute the program just like a standalone script:

$ ./example.rs

Hello, world!

You will notice a small delay on the first execution as the compilation step still has to take place in the background. From the second execution, however, you are just invoking the cached binary from the first execution which rust-script manages for you.

Getting compilation output

Let’s add an argument to rust-script and see what’s going on and where the actual executables and project files are stored:

#!/usr/bin/env -S rust-script --cargo-output

fn main() {
    println!("Hello, world!");
}

Executing the script now, you will see the compilation output and cache location:

$ ./example.rs
   Compiling example_bbede6ff2d210dd974ed45f4 v0.1.0 (/Users/user/Library/Caches/rust-script/projects/bbede6ff2d210dd974ed45f4)
    Finished `release` profile [optimized] target(s) in 0.07s
Hello, world!

The Cargo project creation is abstracted away, but if we want to, we can review the generated Cargo.toml in the projects cache folder:

[[bin]]
name = "example_bbede6ff2d210dd974ed45f4"
path = "/private/tmp/./example.rs"

[dependencies]

[package]
authors = ["Anonymous"]
edition = "2021"
name = "example_bbede6ff2d210dd974ed45f4"
version = "0.1.0"

[profile.release]
strip = true

And the corresponding binary can be found in ~/Library/Caches/rust-script/binaries/release/example_bbede6ff2d210dd974ed45f4.

Using dependencies

As rust-script works with a regular Cargo manifest file in the background, it allows you to easily add dependencies in your single-file scripts as well. Here’s an extended example with the rand crate as dependency:

#!/usr/bin/env -S rust-script --cargo-output

//! ```cargo
//! [dependencies]
//! rand = "0.9.2"
//! ```

use rand;

fn main() {
    let x: u8 = rand::random();
    println!("Random number: {x}");
}

The manifest partial from the doc comment will go into the Cargo.toml as expected. And during the first script execution, the dependencies will be downloaded and compiled just like during a regular build using Cargo. Conveniently, the dependencies are also cached.

Good stuff :-)

Using just Cargo (soon to be built-in Cargo script)

Interestingly, single-file packages will soon also be supported by Cargo itself (currently still listed under “unstable features”).

To reproduce the example (though as a debug build) from above, you have to enable the nightly toolchain, specify the unstable script flag and define dependencies in a fenced frontmatter:

#!/usr/bin/env -S cargo +nightly -Zscript
---cargo
[dependencies]
rand = "0.9.2"
---

use rand;

fn main() {
    let x: u8 = rand::random();
    println!("Random number: {x}");
}

Then, you can execute it just like any other script: ./example_cargo_script.rs:

$ ./example_cargo_script.rs
warning: `package.edition` is unspecified, defaulting to `2024`
    Updating crates.io index
     Locking 16 packages to latest Rust 1.95.0-nightly compatible versions
   Compiling libc v0.2.180
   Compiling getrandom v0.3.4
   Compiling zerocopy v0.8.37
   Compiling cfg-if v1.0.4
   Compiling rand_core v0.9.5
   Compiling ppv-lite86 v0.2.21
   Compiling rand_chacha v0.9.0
   Compiling rand v0.9.2
   Compiling example_cargo_script v0.0.0 (/private/tmp/example_cargo_script.rs)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.45s
     Running `/Users/user/.cargo/build/db/13b301754c8242/target/debug/example_cargo_script`
Random number: 58