packages/omo-codex/plugin/skills/programming/references/rust/one-liners.md
Production hygiene with throwaway ergonomics. Rust scripts get the same strict lints, the same miri rule when unsafe is touched, the same type discipline. The difference is dependency declaration lives inline.
rust-script — the recommended pathInstall once:
cargo install rust-script
Write a script:
#!/usr/bin/env rust-script
//! Fetch a URL and print its body length.
//!
//! Usage:
//! ./fetch.rs <url>
//!
//! ```cargo
//! [dependencies]
//! anyhow = "1"
//! reqwest = { version = "0.12", features = ["blocking"] }
//! ```
use std::env;
fn main() -> anyhow::Result<()> {
let url = env::args().nth(1).context("usage: fetch.rs <url>")?;
let body = reqwest::blocking::get(&url)?.error_for_status()?.text()?;
println!("{} bytes", body.len());
Ok(())
}
Make executable: chmod +x fetch.rs. Run: ./fetch.rs https://example.com.
The //! \``cargoblock is parsed as inlineCargo.toml`. Everything else is normal Rust.
#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! anyhow = "1"
//! tokio = { version = "1", features = ["full"] }
//! reqwest = "0.12"
//! ```
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let urls = [
"https://example.com",
"https://example.org",
];
let client = reqwest::Client::new();
let bodies = futures::future::join_all(urls.iter().map(|u| {
let c = client.clone();
async move { c.get(*u).send().await?.text().await }
})).await;
for (url, body) in urls.iter().zip(bodies) {
match body {
Ok(b) => println!("{url}: {} bytes", b.len()),
Err(e) => eprintln!("{url}: error {e}"),
}
}
Ok(())
}
#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! anyhow = "1"
//! clap = { version = "4", features = ["derive"] }
//! ```
use clap::Parser;
#[derive(Parser, Debug)]
#[command(version, about = "rename files by pattern")]
struct Cli {
/// Glob to match
pattern: String,
/// Replacement template (use {n} for sequence)
template: String,
/// Show what would happen without doing it
#[arg(long)]
dry_run: bool,
}
fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
let entries: Vec<_> = glob::glob(&cli.pattern)?.collect::<Result<_, _>>()?;
for (n, entry) in entries.iter().enumerate() {
let target = cli.template.replace("{n}", &n.to_string());
if cli.dry_run {
println!("{} -> {target}", entry.display());
} else {
std::fs::rename(entry, &target)?;
}
}
Ok(())
}
rust-script caches the compiled binary in ~/.cache/rust-script/. First run is slow (full compile), subsequent runs are instant.
To clear: rust-script --clear-cache.
Pin a script's compile target into the script directory for portability:
rust-script --build-only --base-path . ./script.rs
This drops a target/ next to the script with the prebuilt binary.
cargo-script (RFC 3424, stable since Rust 1.85)The official replacement that landed in cargo proper. Same idea, slightly different syntax:
#!/usr/bin/env -S cargo +nightly -Zscript
---
package:
name = "fetch"
edition = "2024"
dependencies:
anyhow = "1"
reqwest = { version = "0.12", features = ["blocking"] }
---
fn main() -> anyhow::Result<()> {
let url = std::env::args().nth(1).context("url required")?;
println!("{}", reqwest::blocking::get(&url)?.text()?.len());
Ok(())
}
Status as of 2026-05: stabilization in progress. Use rust-script for production now, migrate when cargo script is stable everywhere your tools live.
Add a lints block in the inline Cargo.toml:
//! ```cargo
//! [dependencies]
//! anyhow = "1"
//!
//! [lints.rust]
//! unsafe_code = "forbid"
//!
//! [lints.clippy]
//! all = "deny"
//! pedantic = "warn"
//! unwrap_used = "deny"
//! expect_used = "deny"
//! panic = "deny"
//! ```
Now the script gets the same strictness as the main project. If you need a one-line unwrap() for prototype velocity, switch the lint to warn for that one script - never blanket allow.
Run with lints visible:
RUSTFLAGS="-D warnings" rust-script ./script.rs
cargo new --bin.build.rs) → real crate.cargo dist.#[test]s under cargo test, but the workflow is awkward).A reasonable migration path: start as a script, when complexity grows past ~200 lines or you reach for a second .rs file, run rust-script --emit ./script.rs to dump a regular Cargo project skeleton and continue from there.
#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! ```
fn double(x: i32) -> i32 { x * 2 }
fn main() {
println!("{}", double(21));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn doubles_ints() {
assert_eq!(double(5), 10);
}
}
Run tests: rust-script --test ./script.rs.
For ad-hoc data processing on stdin:
#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! serde_json = "1"
//! ```
use std::io::{self, BufRead, Write};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let stdin = io::stdin();
let stdout = io::stdout();
let mut out = stdout.lock();
for line in stdin.lock().lines() {
let line = line?;
let value: serde_json::Value = serde_json::from_str(&line)?;
if let Some(s) = value.get("level").and_then(|v| v.as_str()) {
if s == "error" {
writeln!(out, "{line}")?;
}
}
}
Ok(())
}
cat logs.jsonl | ./filter-errors.rs — filter JSON logs by level == "error". Faster than jq for big files, type-safe.
For numerics:
#!/usr/bin/env rust-script
//! sum a column of numbers from stdin
use std::io::{self, BufRead};
fn main() {
let total: f64 = io::stdin().lock().lines()
.filter_map(|l| l.ok())
.filter_map(|l| l.trim().parse::<f64>().ok())
.sum();
println!("{total}");
}
rust-script shebang trick on macOSmacOS does not support multi-arg shebangs without env -S. Use:
#!/usr/bin/env -S rust-script --
The -- lets clap-style argument parsers see the user's args, not the rust-script arguments.
VS Code / Helix / Vim with rust-analyzer: open the script file as if it were src/main.rs of an inferred crate. Most editors auto-detect the inline manifest. If not, hand-create a Cargo.toml next to the script with matching deps for the duration of editing, then delete it.
rust-script is too heavyFor absolutely throwaway "one expression on stdin" use cases, a Rust REPL like evcxr_jupyter (Jupyter kernel) or irust (terminal REPL) is more appropriate:
cargo install irust
irust
But these are interactive playgrounds, not scriptable. For shell pipelines, stay with rust-script.
Same strict lints. Same clippy::pedantic enforcement. Same unsafe-requires-SAFETY rule. The agent does not get a free pass on a 30-line script. The whole point of strict scripts is that production hygiene is cheap when the toolchain enforces it.