Skip to content
shellhacks.dev
Go

Go vs Rust vs Python for Command Line Tools With Examples

Compare Go, Rust and Python for command line tools: startup time, distribution, development speed, safety and the same small tool in each language.

6 min read
Summarize in AI
Illustration for "Go vs Rust vs Python for Command Line Tools With Examples": a terminal session, a loop arrow, progress bars.

Three languages cover most new command line tools, and arguments about them usually forget what the tool needs to do. This comparison uses the same small program in each, a counter of lines matching a word, and focuses on what differs in practice. It builds on the case for Go over Bash. The versions assumed are Go 1.23, Rust 1.82 and Python 3.13.

The same tool three ways

Go
package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func main() {
	word := os.Args[1]
	n := 0
	sc := bufio.NewScanner(os.Stdin)
	for sc.Scan() {
		if strings.Contains(sc.Text(), word) {
			n++
		}
	}
	fmt.Println(n)
}
Text
// Rust
use std::io::{self, BufRead};

fn main() {
    let word = std::env::args().nth(1).expect("usage: count WORD");
    let n = io::stdin().lock().lines().map_while(Result::ok).filter(|l| l.contains(&word)).count();
    println!("{n}");
}
Python
import sys

word = sys.argv[1]
print(sum(1 for line in sys.stdin if word in line))

Python is shortest, Go is plain and explicit and Rust packs the logic into iterators with compile-time guarantees. All three read standard input as a stream, so they work in a pipeline with the tools in reading from stdin in Go.

Where they differ

Text
                    Go          Rust        Python
Hello binary        ~1.5 MB     ~0.4 MB     needs interpreter
Release build time  seconds     tens of s   none
Startup             ~1-2 ms     ~1 ms       ~15-30 ms
Distribution        one file    one file    pip/venv/zipapp
Memory safety       GC          compile-time  GC
Learning curve      gentle      steep       gentlest

The figures are typical orders of magnitude, not benchmarks from a particular machine. Python startup is paid on every invocation, which matters for a tool called hundreds of times in a loop, and not at all for one run per day. Rust's longer compile times affect iteration speed. Go sits in the middle with a fast compiler and a small standard library that already includes HTTP, JSON and testing.

Distribution decides more than speed

A Go or Rust binary is built once per platform and copied. A Python tool needs an interpreter of a compatible version and its dependencies, handled with pipx, uv tool install or a zipapp, which is acceptable for developers and painful for end users. If the audience is your own team with Python everywhere, Python wins on development speed. If it ships to customers or minimal containers, compiled languages win. Releasing is a solved problem for Go in GoReleaser, and cross builds are covered in cross compiling.

Libraries for CLI work

Go has flag in the standard library, plus Cobra and others compared in Cobra versus urfave cli. Rust has the widely used clap with derive macros, which generates help, completions and validation. Python has argparse built in, and Click and Typer for richer interfaces. All three have good support for colours, progress bars and tables, so the library ecosystem rarely decides.

What the comparison measures

Choosing a language for a command line tool is a trade-off among several properties, and which one dominates depends on the project. Python gives the fastest path from idea to working tool, a rich standard library and a huge package ecosystem, and its cost is the need for an interpreter and dependencies on the target machine, plus a slower start. Go compiles to a single static binary with fast startup and straightforward cross-compilation, with a smaller ecosystem for niche tasks and more code for the same logic. Rust offers the same single-binary distribution with the highest runtime performance and strong guarantees about memory and error handling, at the price of a steeper learning curve and longer compile times.

Notice that none of these axes is raw execution speed for most tools. A CLI that reads a file and prints a summary is limited by IO and process startup, so a difference between languages shows only on heavy computation.

Startup time and installation

Startup time shapes how a tool feels. A Python script that imports several libraries can take a couple of hundred milliseconds before doing anything, which is noticeable when the tool runs in a shell prompt or inside a loop. Go and Rust binaries start in a few milliseconds. For a tool called thousands of times from scripts, that difference adds up to real time. Installation matters even more for users: copying one file is trivial, while installing a Python tool means choosing between pipx, virtual environments and system packages, and handling the interpreter version. Packaging tools such as PyInstaller or shiv can produce a single file from Python, with larger size and some compatibility quirks.

Consider who the users are. Developers with Python already installed will accept a pip install. Operators on locked-down servers prefer a binary they can verify and drop in place.

Maintenance and the team

The tool outlives its author's enthusiasm, so the team's skills matter. If most people on the team write Python, a Python tool will be maintained, and a Rust one may become an orphan. If the organisation already ships Go services, a Go CLI reuses the build, release and review practice. Consider the quality of tooling: Go has a formatter, test runner and race detector in the standard toolchain, Rust has Cargo with excellent testing and linting, and Python depends on a combination of third-party tools whose choices vary between projects. Type checking in Python is optional and incremental, while Go and Rust enforce types at compile time, which reduces a class of runtime errors in tools run unattended.

Think about the interface stability too. A tool that other scripts call must keep its flags and exit codes steady, whatever the language.

A way to decide

For a prototype, a one-person tool or glue around libraries that only exist in Python, choose Python. For a tool distributed to many machines, run by operators, with moderate complexity and a need for concurrency, choose Go. For a tool where performance or correctness is critical, such as parsing large files or processing untrusted data, choose Rust. Prototype the core in the language you know best, measure startup and the main workload on realistic data, and look at the distribution story before committing. It is acceptable to start in Python and rewrite in Go once the interface has settled, as long as the interface is documented and tested, so the rewrite is a swap of the engine. The decision should be revisited when the constraints change, not defended out of habit.

Whichever language wins, invest in the parts users touch: clear help text, consistent flags, meaningful exit codes, and error messages that say what to do next. These matter more to adoption than the language underneath. A tool that is pleasant and predictable gets used, and one with a clever implementation and a confusing interface does not. Write the interface down first, as a short specification with examples, and the language choice becomes a smaller decision.

Where this does not apply

If the tool is a thin wrapper around other commands, Bash is shorter than any of them, and if it needs heavy numerical or data work Python's ecosystem is hard to beat. Performance-critical tools such as search or compression benefit from Rust or Go's speed, but most CLI tools wait on disks and networks, so language speed is rarely the bottleneck. Hiring and team familiarity trump language features after the first month. See the Go documentation, the Rust Book and the argparse reference.

Also inPython
Share

Keep reading

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.