Rust From the Ground Up

The Rust CLI Programming book by Matthew Provost

New! Updated for the 2021 Edition of Rust and clap version 3

In 2022, developers voted for Rust as their most loved programming language for the seventh consecutive year. And Rust is tied with Python as the technology that they would most want to use.

This book is for "day one" Rust programmers who want to achieve fluency in idiomatic Rust by implementing real-world programs, without fighting the borrow checker. Each example-heavy chapter implements a single CLI program in Rust based on the original BSD Unix source code. (No experience with programming in C required!).

"Rust From the Ground Up" flattens Rust's learning curve, demonstrating how to build practical, real-world applications which handle realistic conditions such as edge cases and errors. Every chapter in Rust From the Ground Up takes a “show, don’t tell” approach: examining the original BSD sources for a well-known Unix CLI program and introducing enough Rust to build a fully functional copy. After reading this book, you will have gained the confidence and intuition to write production-quality Rust programs.


Rust's most distinctive feature is its ownership system, which guarantees memory safety. It's possible to disable certain safety checks by designating a section of Rust code as unsafe (usually for performance reasons), but all of the code in this book is safe.


Its ownership system makes Rust particularly well suited for replacing existing programs written in the C language, which is notorious for producing programs with bugs caused by its lack of memory safety. Apple, Microsoft, and Google have reported that at least 70% of the security vulnerabilities in their C/C++ codebases are caused by memory safety bugs.

Borrow Checker

The Rust compiler's borrow checker has a reputation as being difficult to work with. The borrow checker validates the ownership and lifetimes of values in memory at compile time, without a garbage collector. New Rust programmers often feel like they are "fighting the borrow checker" until they build intuition about how ownership works.


Understanding ownership is hard, and sometimes the easiest way to get a Rust program to compile is by copying a value in memory, or using reference counting. This book tackles ownership head on, demonstrating clear strategies for resolving common issues with the borrow checker, enabling the reader to become productive and to write fully functional programs in Rust, without overwhelming them with more advanced concepts such as lifetimes.

Real World Code

The goal of this book is to teach a programmer with no Rust experience how to write production quality, idiomatic Rust code that safely handles real world edge cases and errors. There are no linked list exercises, made up examples (like FizzBuzz), demos (Mandelbrot), or sample code. Every chapter demonstrates how to write a real program by examining the original C source code for a classic BSD Unix CLI utility and incrementally building a fully functional version in Rust.

No C Required

Rust's syntax is designed to be familiar to programmers accustomed to languages such as C, C++, or Java, with curly braces {} around blocks of code, comments starting with // or surrounded by /* and */, and statements terminated with ;. But this book doesn't require specific experience in C programming; each chapter explains the syntax and functions used in the read-only C examples.


Each chapter rewrites a C program in stylish, idiomatic Rust. Instead of mechanical, line by line translations, the book emphasizes a functional programming style, heavily focusing on iterators and avoiding mutable state. For example, rewriting the head program (which prints the first 10 lines from each filename argument):


#include <stdio.h>
#include <stdlib.h>

main(int argc, char *argv[])
    FILE    *fp;
    int     ch;
    long    cnt, linecnt = 10;

    for (argv++; *argv; ++argv) {
        if ((fp = fopen(*argv, "r")) != NULL) {
            for (cnt = linecnt; cnt && !feof(fp); --cnt) {
                while ((ch = getc(fp)) != EOF) {
                    if (putchar(ch) == '\n') {

use std::env::args;
use std::fs::File;
use std::io::{BufRead, BufReader};

fn main() {
    let args = args().skip(1);
    let files = args.map(|arg| {
    let linecnt = 10;

    for file in files {
        for line in file.lines().take(linecnt).flatten() {
            println!("{}", line);

Table of Contents