contents

preface

acknowledgments

about this book

about the author

about the cover illustration

  

  1 Introducing Rust

  1.1  Where is Rust used?

  1.2  Advocating for Rust at work

  1.3  A taste of the language

Cheating your way to “Hello, world!”

Your first Rust program

  1.4  Downloading the book’s source code

  1.5  What does Rust look and feel like?

  1.6  What is Rust?

Goal of Rust: Safety

Goal of Rust: Productivity

Goal of Rust: Control

  1.7  Rust’s big features

Performance

Concurrency

Memory efficiency

  1.8  Downsides of Rust

Cyclic data structures

Compile times

Strictness

Size of the language

Hype

  1.9  TLS security case studies

Heartbleed

Goto fail;

  1.10 Where does Rust fit best?

Command-line utilities

Data processing

Extending applications

Resource-constrained environments

Server-side applications

Desktop applications

Desktop

Mobile

Web

Systems programming

  1.11 Rust’s hidden feature: Its community

  1.12 Rust phrase book

Part 1 Rust language distinctives

  2 Language foundations

  2.1  Creating a running program

Compiling single files with rustc

Compiling Rust projects with cargo

  2.2  A glance at Rust’s syntax

Defining variables and calling functions

  2.3  Numbers

Integers and decimal (floating-point) numbers

Integers with base 2, base 8, and base 16 notation

Comparing numbers

Rational, complex numbers, and other numeric types

  2.4  Flow control

For: The central pillar of iteration

Continue: Skipping the rest of the current iteration

While: Looping until a condition changes its state

Loop: The basis for Rust’s looping constructs

Break: Aborting a loop

If, if else, and else: Conditional branching

Match: Type-aware pattern matching

  2.5  Defining functions

  2.6  Using references

  2.7  Project: Rendering the Mandelbrot set

  2.8  Advanced function definitions

Explicit lifetime annotations

Generic functions

  2.9  Creating grep-lite

  2.10 Making lists of things with arrays, slices, and vectors

Arrays

Slices

Vectors

  2.11 Including third-party code

Adding support for regular expressions

Generating the third-party crate documentation locally

Managing Rust toolchains with rustup

  2.12 Supporting command-line arguments

  2.13 Reading from files

  2.14 Reading from stdin

  3 Compound data types

  3.1  Using plain functions to experiment with an API

  3.2  Modeling files with struct

  3.3  Adding methods to a struct with impl

Simplifying object creation by implementing new()

  3.4  Returning errors

Modifying a known global variable

Making use of the Result return type

  3.5  Defining and making use of an enum

Using an enum to manage internal state

  3.6  Defining common behavior with traits

Creating a Read trait

Implementing std::fmt::Display for your own types

  3.7  Exposing your types to the world

Protecting private data

  3.8  Creating inline documentation for your projects

Using rustdoc to render docs for a single source file

Using cargo to render docs for a crate and its dependencies

  4 Lifetimes, ownership, and borrowing

  4.1  Implementing a mock CubeSat ground station

Encountering our first lifetime issue

Special behavior of primitive types

  4.2  Guide to the figures in this chapter

  4.3  What is an owner? Does it have any responsibilities?

  4.4  How ownership moves

  4.5  Resolving ownership issues

Use references where full ownership is not required

Use fewer long-lived values

Duplicate the value

Wrap data within specialty types

Part 2 Demystifying systems programming

  5 Data in depth

  5.1  Bit patterns and types

  5.2  Life of an integer

Understanding endianness

  5.3  Representing decimal numbers

  5.4  Floating-point numbers

Looking inside an f32

Isolating the sign bit

Isolating the exponent

Isolate the mantissa

Dissecting a floating-point number

  5.5  Fixed-point number formats

  5.6  Generating random probabilities from random bytes

  5.7  Implementing a CPU to establish that functions are also data

CPU RIA/1: The Adder

Full code listing for CPU RIA/1: The Adder

CPU RIA/2: The Multiplier

CPU RIA/3: The Caller

CPU 4: Adding the rest

  6 Memory

  6.1  Pointers

  6.2  Exploring Rust’s reference and pointer types

Raw pointers in Rust

Rust’s pointer ecosystem

Smart pointer building blocks

  6.3  Providing programs with memory for their data

The stack

The heap

What is dynamic memory allocation?

Analyzing the impact of dynamic memory allocation

  6.4  Virtual memory

Background

Step 1: Having a process scan its own memory

Translating virtual addresses to physical addresses

Step 2: Working with the OS to scan an address space

Step 3: Reading from and writing to process memory

  7 Files and storage

  7.1  What is a file format?

  7.2  Creating your own file formats for data storage

Writing data to disk with serde and the bincode format

  7.3  Implementing a hexdump clone

  7.4  File operations in Rust

Opening a file in Rust and controlling its file mode

Interacting with the filesystem in a type-safe manner with std::fs::Path

  7.5  Implementing a key-value store with a log-structured, append-only storage architecture

The key-value model

Introducing actionkv v1: An in-memory key-value store with a command-line interface

  7.6  Actionkv v1: The front-end code

Tailoring what is compiled with conditional compilation

  7.7  Understanding the core of actionkv: The libactionkv crate

Initializing the ActionKV struct

Processing an individual record

Writing multi-byte binary data to disk in a guaranteed byte order

Validating I/O errors with checksums

Inserting a new key-value pair into an existing database

The full code listing for actionkv

Working with keys and values with HashMap and BTreeMap

Creating a HashMap and populating it with values

Retrieving values from HashMap and BTreeMap

How to decide between HashMap and BTreeMap

Adding a database index to actionkv v2.0

  8 Networking

  8.1  All of networking in seven paragraphs

  8.2  Generating an HTTP GET request with reqwest

  8.3  Trait objects

What do trait objects enable?

What is a trait object?

Creating a tiny role-playing game: The rpg project

  8.4  TCP

What is a port number?

Converting a hostname to an IP address

  8.5  Ergonomic error handling for libraries

Issue: Unable to return multiple error types

Wrapping downstream errors by defining our own error type

Cheating with unwrap() and expect()

  8.6  MAC addresses

Generating MAC addresses

  8.7  Implementing state machines with Rust’s enums

  8.8  Raw TCP

  8.9  Creating a virtual networking device

  8.10 “Raw” HTTP

  9 Time and timekeeping

  9.1  Background

  9.2  Sources of time

  9.3  Definitions

  9.4  Encoding time

Representing time zones

  9.5  clock v0.1.0: Teaching an application how to tell the time

  9.6  clock v0.1.1: Formatting timestamps to comply with ISO 8601 and email standards

Refactoring the clock v0.1.0 code to support a wider architecture

Formatting the time

Providing a full command-line interface

clock v0.1.1: Full project

  9.7  clock v0.1.2: Setting the time

Common behavior

Setting the time for operating systems that use libc

Setting the time on MS Windows

clock v0.1.2: The full code listing

  9.8  Improving error handling

  9.9  clock v0.1.3: Resolving differences between clocks with the Network Time Protocol (NTP)

Sending NTP requests and interpreting responses

Adjusting the local time as a result of the server’s response

Converting between time representations that use different precisions and epochs

clock v0.1.3: The full code listing

10 Processes, threads, and containers

10.1  Anonymous functions

10.2  Spawning threads

Introduction to closures

Spawning a thread

Effect of spawning a few threads

Effect of spawning many threads

Reproducing the results

Shared variables

10.3  Differences between closures and functions

10.4  Procedurally generated avatars from a multithreaded parser and code generator

How to run render-hex and its intended output

Single-threaded render-hex overview

Spawning a thread per logical task

Using a thread pool and task queue

10.5  Concurrency and task virtualization

Threads

What is a context switch?

Processes

WebAssembly

Containers

Why use an operating system (OS) at all?

11 Kernel

11.1  A fledgling operating system (FledgeOS)

Setting up a development environment for developing an OS kernel

Verifying the development environment

11.2  Fledgeos-0: Getting something working

First boot

Compilation instructions

Source code listings

Panic handling

Writing to the screen with VGA-compatible text mode

_start(): The main() function for FledgeOS

11.3  fledgeos-1: Avoiding a busy loop

Being power conscious by interacting with the CPU directly

fledgeos-1 source code

11.4  fledgeos-2: Custom exception handling

Handling exceptions properly, almost

fledgeos-2 source code

11.5  fledgeos-3: Text output

Writing colored text to the screen

Controlling the in-memory representation of enums

Why use enums?

Creating a type that can print to the VGA frame buffer

Printing to the screen

fledgeos-3 source code

11.6  fledgeos-4: Custom panic handling

Implementing a panic handler that reports the error to the user

Reimplementing panic() by making use of core::fmt::Write

Implementing core::fmt::Write

fledge-4 source code

12 Signals, interrupts, and exceptions

12.1  Glossary

Signals vs. interrupts

12.2  How interrupts affect applications

12.3  Software interrupts

12.4  Hardware interrupts

12.5  Signal handling

Default behavior

Suspend and resume a program’s operation

Listing all signals supported by the OS

12.6  Handling signals with custom actions

Global variables in Rust

Using a global variable to indicate that shutdown has been initiated

12.7  Sending application-defined signals

Understanding function pointers and their syntax

12.8  Ignoring signals

12.9  Shutting down from deeply nested call stacks

Introducing the sjlj project

Setting up intrinsics in a program

Casting a pointer to another type

Compiling the sjlj project

sjlj project source code

12.10 A note on applying these techniques to platforms without signals

12.11 Revising exceptions

  

index