📄 Page
1
Ken Youens-Clark Command-Line Rust A Project-Based Primer for Writing Rust CLIs
📄 Page
2
(This page has no text content)
📄 Page
3
Ken Youens-Clark Command-Line Rust A Project-Based Primer for Writing Rust CLIs Boston Farnham Sebastopol TokyoBeijing
📄 Page
4
978-1-098-10943-1 [LSI] Command-Line Rust by Ken Youens-Clark Copyright © 2022 Charles Kenneth Youens-Clark. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Acquisitions Editor: Suzanne McQuade Development Editors: Rita Fernando and Corbin Collins Production Editors: Caitlin Ghegan and Gregory Hyman Copyeditor: Kim Sandoval Proofreader: Rachel Head Indexer: Ellen Troutman-Zaig Interior Designer: David Futato Cover Designer: Karen Montgomery Illustrator: Kate Dullea January 2022: First Edition Revision History for the First Edition 2021-01-13: First Release See http://oreilly.com/catalog/errata.csp?isbn=9781098109431 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Command-Line Rust, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. The views expressed in this work are those of the author and do not represent the publisher’s views. While the publisher and the author have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the author disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights.
📄 Page
5
Table of Contents Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix 1. Truth or Consequences. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Getting Started with “Hello, world!” 1 Organizing a Rust Project Directory 3 Creating and Running a Project with Cargo 4 Writing and Running Integration Tests 6 Adding a Project Dependency 10 Understanding Program Exit Values 11 Testing the Program Output 14 Exit Values Make Programs Composable 15 Summary 16 2. Test for Echo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 How echo Works 17 Getting Started 20 Accessing the Command-Line Arguments 21 Adding clap as a Dependency 23 Parsing Command-Line Arguments Using clap 25 Creating the Program Output 29 Writing Integration Tests 33 Creating the Test Output Files 34 Comparing Program Output 35 Using the Result Type 36 Summary 41 iii
📄 Page
6
3. On the Catwalk. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 How cat Works 44 Getting Started 48 Starting with Tests 48 Creating a Library Crate 50 Defining the Parameters 51 Iterating Through the File Arguments 56 Opening a File or STDIN 56 Using the Test Suite 59 Solution 63 Reading the Lines in a File 63 Printing Line Numbers 64 Going Further 67 Summary 67 4. Head Aches. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 How head Works 70 Getting Started 73 Writing a Unit Test to Parse a String into a Number 75 Converting Strings into Errors 77 Defining the Arguments 80 Processing the Input Files 83 Reading Bytes Versus Characters 85 Solution 86 Reading a File Line by Line 86 Preserving Line Endings While Reading a File 86 Reading Bytes from a File 88 Printing the File Separators 91 Going Further 92 Summary 92 5. Word to Your Mother. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 How wc Works 95 Getting Started 100 Iterating the Files 105 Writing and Testing a Function to Count File Elements 106 Solution 109 Counting the Elements of a File or STDIN 109 Formatting the Output 111 Going Further 117 Summary 117 iv | Table of Contents
📄 Page
7
6. Den of Uniquity. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 How uniq Works 119 Getting Started 124 Defining the Arguments 125 Testing the Program 129 Processing the Input Files 133 Solution 134 Going Further 139 Summary 140 7. Finders Keepers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 How find Works 142 Getting Started 146 Defining the Arguments 147 Validating the Arguments 153 Finding All the Things 155 Solution 157 Conditionally Testing on Unix Versus Windows 163 Going Further 166 Summary 167 8. Shave and a Haircut. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 How cut Works 169 Getting Started 174 Defining the Arguments 175 Parsing the Position List 181 Extracting Characters or Bytes 187 Parsing Delimited Text Files 189 Solution 191 Selecting Characters from a String 191 Selecting Bytes from a String 193 Selecting Fields from a csv::StringRecord 195 Final Boss 196 Going Further 198 Summary 198 9. Jack the Grepper. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 How grep Works 202 Getting Started 205 Defining the Arguments 206 Finding the Files to Search 212 Table of Contents | v
📄 Page
8
Finding the Matching Lines of Input 215 Solution 219 Going Further 223 Summary 223 10. Boston Commons. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 How comm Works 225 Getting Started 229 Defining the Arguments 229 Validating and Opening the Input Files 233 Processing the Files 235 Solution 236 Going Further 244 Summary 244 11. Tailor Swyfte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 How tail Works 245 Getting Started 250 Defining the Arguments 250 Parsing Positive and Negative Numeric Arguments 255 Using a Regular Expression to Match an Integer with an Optional Sign 256 Parsing and Validating the Command-Line Arguments 260 Processing the Files 262 Counting the Total Lines and Bytes in a File 262 Finding the Starting Line to Print 264 Finding the Starting Byte to Print 265 Testing the Program with Large Input Files 266 Solution 267 Counting All the Lines and Bytes in a File 267 Finding the Start Index 268 Printing the Lines 269 Printing the Bytes 271 Benchmarking the Solution 273 Going Further 275 Summary 275 12. Fortunate Son. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 How fortune Works 278 Getting Started 281 Defining the Arguments 282 Finding the Input Sources 288 vi | Table of Contents
📄 Page
9
Reading the Fortune Files 291 Randomly Selecting a Fortune 293 Printing Records Matching a Pattern 295 Solution 296 Going Further 301 Summary 301 13. Rascalry. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303 How cal Works 303 Getting Started 306 Defining and Validating the Arguments 307 Writing the Program 318 Solution 321 Going Further 326 Summary 326 14. Elless Island. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 How ls Works 330 Getting Started 332 Defining the Arguments 333 Finding the Files 336 Formatting the Long Listing 341 Displaying Octal Permissions 343 Testing the Long Format 346 Solution 349 Notes from the Testing Underground 355 Going Further 358 Summary 359 Epilogue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363 Table of Contents | vii
📄 Page
10
(This page has no text content)
📄 Page
11
Preface I already know the ending \ It’s the part that makes your face implode —They Might Be Giants, “Experimental Film” (2004) I remember back when this new language called “JavaScript” came out in 1995. A few years later, I decided to learn it, so I bought a big, thick reference book and read it cover to cover. The book was well written and thoroughly explained the language in great detail, from strings to lists and objects. But when I finished the book, I still couldn’t write JavaScript to save my life. Without applying this knowledge by writing programs, I learned very little. I’ve since improved at learning how to learn a lan‐ guage, which is perhaps the most valuable skill you can develop as a programmer. For me, that means rewriting programs I already know, like tic-tac-toe. Rust is the new kid on the block now, and perhaps you’ve picked up this book to see what it’s all about. This book is not a reference on the language. Those already exist, and they’re quite good. Instead, I’ve written a book that challenges you to write many small programs that probably will be familiar to you. Rust is reputed to have a fairly steep learning curve, but I believe this approach will help you quickly become pro‐ ductive with the language. Specifically, you’re going to write Rust versions of core Unix command-line tools such as head and cal. This will teach you more about the tools and why they are so wildly useful while also providing the context to use Rust concepts like strings, vec‐ tors, and filehandles. If you are not familiar with Unix or command-line program‐ ming, then you will learn about concepts like program exit values, command-line arguments, output redirection, pipes to connect one program’s output (STDOUT or standard out) to the input of another program (STDIN or standard in), and how to use STDERR (standard error) to segregate error messages from other output. The programs you write will reveal patterns that you’ll be able to use when you create your own Rust programs—patterns like validating parameters, reading and writing files, parsing text, and using regular expressions. Many of these tools and concepts don’t even exist on ix
📄 Page
12
Windows, so users of that platform will create decent versions of several core Unix programs. What Is Rust (and Why Is Everybody Talkin’ About It)? Rust is “a language empowering everyone to build reliable and efficient software.” Rust was created by Graydon Hoare and many others around 2006, while Hoare was working at Mozilla Research. It gained enough interest and users that by 2010 Mozilla had sponsored the development effort. In the 2021 Stack Overflow Developer Survey, nearly 80,000 developers ranked Rust as the “most loved” language for the sixth year running. Figure P-1. Here is a logo I made from an old Rush logo. As a kid playing the drums in the 1980s, I listened to a lot of Rush. Anyway, Rust is cool, and this logo proves it. The language is syntactically similar to C, so you’ll find things like for loops, semicolon-terminated statements, and curly braces denoting block structures. Cru‐ cially, Rust can guarantee memory safety through the use of a borrow checker that tracks which part of a program has safe access to different parts of memory. This safety does not come at the expense of performance, though. Rust programs compile to native binaries and often match or beat the speed of programs written in C or C++. For this reason, Rust is often described as a systems programming language that has been designed for performance and safety. Rust is a statically typed language like C/C++ or Java. This means that a variable can never change its type, such as from a number to a string, for example. You don’t always have to declare a variable’s type in Rust because the compiler can often figure it out from the context. This is in contrast to dynamically typed languages like Perl, JavaScript, or Python, where a variable can change its type at any point in the pro‐ gram, like from a string to a filehandle. x | Preface
📄 Page
13
Rust is not an object-oriented (OO) language like C++ or Java, as there are no classes or inheritance in Rust. Instead, Rust uses a struct (structure) to represent complex data types and traits to describe how types can behave. These structures can have methods, can mutate the internal state of the data, and might even be called objects in the documentation, but they are not objects in the formal sense of the word. Rust has borrowed many exciting concepts from other languages and programming paradigms, including purely functional languages such as Haskell. For instance, vari‐ ables in Rust are immutable by default, meaning they can’t be changed from their ini‐ tial value; you have to specifically inform the compiler that they are mutable. Functions are also first-class values, which means they can be passed as arguments to other so-called higher-order functions. Most exciting to my mind is Rust’s use of enu‐ merated and sum types, also called algebraic data types (ADTs), which allow you to represent, for instance, that a function can return a Result that can be either an Ok containing some value or an Err containing some other kind of value. Any code that deals with these values must handle all possibilities, so you’re never at risk of forget‐ ting to handle an error that could unexpectedly crash your program. Who Should Read This Book You should read this book if you want to learn the basics of the Rust language by writing practical command-line programs that address common programming tasks. I imagine most readers will already know some basics about programming from at least one other language. For instance, you probably know about creating variables, using loops to repeat an action, creating functions, and so forth. I imagine that Rust might be a difficult first language as it uses types extensively and requires under‐ standing some fine details about computer memory. I also assume you have at least some idea of how to use the command line and know some basic Unix commands, like how to create, remove, and change into directories. This book will focus on the practical side of things, showing you what you need to know to get things done. I’ll leave the nitty-gritty to more comprehensive books such as Programming Rust, 2nd ed., by Jim Blandy, Jason Orendorff, and Leonora F. S. Tindall (O’Reilly) and The Rust Programming Language by Steve Klabnik and Carol Nichols (No Starch Press). I highly recommend that you read one or both of those along with this book to dig deeper into the language itself. You should also read this book if you’d like to see how to write and run tests to check Rust programs. I’m an advocate for using tests not only to verify that programs work properly but also as an aid to breaking a problem into small, understandable, testable parts. I will demonstrate how to use tests that I have provided for you as well as how to use test-driven development (TDD), where you write tests first and then write code that passes those tests. I hope that this book will show that the strictness of the Rust Preface | xi
📄 Page
14
compiler combined with testing leads to better programs that are easier to maintain and modify. Why You Should Learn Rust There are plenty of reasons to learn Rust. First, I find that Rust’s type checking pre‐ vents me from making many basic errors. My background is in more dynamically typed languages like Perl, Python, and JavaScript where there is little to no checking of types. The more I used statically typed languages like Rust, the more I realized that dynamically typed languages force much more work onto me, requiring me to verify my programs and write many more tests. I gradually came to feel that the Rust com‐ piler, while very strict, was my dance partner and not my enemy. Granted, it’s a dance partner who will tell you every time you step on their toes or miss a cue, but that eventually makes you a better dancer, which is the goal after all. Generally speaking, when I get a Rust program to compile, it usually works as I intended. Second, it’s easy to share a Rust program with someone who doesn’t know Rust or is not a developer at all. If I write a Python program for a workmate, I must give them the Python source code to run and ensure they have the right version of Python and all the required modules to execute my code. In contrast, Rust programs are compiled directly into a machine-executable file. I can write and debug a program on my machine, build an executable for the architecture it needs to run on, and give my col‐ league a copy of the program. Assuming they have the correct architecture, they will not need to install Rust and can run the program directly. Third, I often build containers using Docker or Singularity to encapsulate workflows. I find that the containers for Rust programs are often orders of magnitude smaller than those for Python programs. For instance, a Docker container with the Python runtime may require several hundred MB. In contrast, I can build a bare-bones Linux virtual machine with a Rust binary that may only be tens of MB in size. Unless I really need some particular features of Python, such as machine learning or natural language processing modules, I prefer to write in Rust and have smaller, leaner containers. Finally, I find that I’m extremely productive with Rust because of the rich ecosystem of available modules. I have found many useful Rust crates—which is what libraries are called in Rust—on crates.io, and the documentation at Docs.rs is thorough and easy to navigate. The Coding Challenges In this book, you will learn how to write and test Rust code by creating complete pro‐ grams. Each chapter will show you how to start a program from scratch, add features, work through error messages, and test your logic. I don’t want you to passively read xii | Preface
📄 Page
15
this book on the bus to work and put it away. You will learn the most by writing your own solutions, but I believe that even typing the source code I present will prove beneficial. The problems I’ve selected for this book hail from the Unix command-line coreutils, because I expect these will already be quite familiar to many readers. For instance, I assume you’ve used head and tail to look at the first or last few lines of a file, but have you ever written your own versions of these programs? Other Rustaceans (peo‐ ple who use Rust) have had the same idea, so there are plenty of other Rust imple‐ mentations of these programs you can find on the internet. Beyond that, these are fairly small programs that each lend themselves to teaching a few skills. I’ve sequenced the projects so that they build upon one another, so it’s probably best if you work through the chapters in order. One reason I’ve chosen many of these programs is that they provide a sort of ground truth. While there are many flavors of Unix and many implementations of these pro‐ grams, they usually all work the same and produce the same results. I use macOS for my development, which means I’m running mostly the BSD (Berkeley Standard Dis‐ tribution) or GNU (GNU’s Not Unix) variants of these programs. Generally speaking, the BSD versions predate the GNU versions and have fewer options. For each chal‐ lenge program, I use a shell script to redirect the output from the original program into an output file. The goal is then to have the Rust programs create the same output for the same inputs. I’ve been careful to include files encoded on Windows as well as simple ASCII text mixed with Unicode characters to force my programs to deal with various ideas of line endings and characters in the same way as the original programs. For most of the challenges, I’ll try to implement only a subset of the original pro‐ grams as they can get pretty complicated. I also have chosen to make a few small changes in the output from some of the programs so that they are easier to teach. Consider this to be like learning to play an instrument by playing along with a recording. You don’t have to play every note from the original version. The important thing is to learn common patterns like handling arguments and reading inputs so you can move on to writing your material. As a bonus challenge, try writing these pro‐ grams in other languages so you can see how the solutions differ from Rust. Getting Rust and the Code To start, you’ll need to install Rust. One of my favorite parts about Rust is the ease of using the rustup tool for installing, upgrading, and managing Rust. It works equally well on Windows and Unix-type operating systems (OSs) like Linux and macOS. You will need to follow the installation instructions for your OS. If you have already installed rustup, you might want to run rustup update to get the latest version of the language and tools, as Rust updates about every six weeks. Execute rustup doc to Preface | xiii
📄 Page
16
read copious volumes of documentation. You can check your version of the rustc compiler with the following command: $ rustc --version rustc 1.56.1 (59eed8a2a 2021-11-01) All the tests, data, and solutions for the programs can be found in the book’s GitHub repository. You can use the Git source code management tool (which you may need to install) to copy this to your machine. The following command will create a new directory on your computer called command-line-rust with the contents of the book’s repository: $ git clone https://github.com/kyclark/command-line-rust.git You should not write your code in the directory you cloned in the preceding step. You should create a separate directory elsewhere for your projects. I suggest that you cre‐ ate your own Git repository to hold the programs you’ll write. For example, if you use GitHub and call it rust-solutions, then you can use the following command to clone your repository. Be sure to replace YOUR_GITHUB_ID with your actual GitHub ID: $ git clone https://github.com/YOUR_GITHUB_ID/rust-solutions.git One of the first tools you will encounter in Rust is Cargo, which is its build tool, package manager, and test runner. Each chapter will instruct you to create a new project using Cargo, and I recommend that you do this inside your solutions direc‐ tory. You will copy each chapter’s tests directory from the book’s repository into your project directory to test your code. If you’re curious what testing code looks like with Cargo and Rust, you can run the tests for Chapter 1. Change into the book’s 01_hello directory and run the tests with cargo test: $ cd command-line-rust/01_hello $ cargo test If all goes well, you should see some passing tests (in no particular order): running 3 tests test false_not_ok ... ok test true_ok ... ok test runs ... ok I tested all the programs on macOS, Linux, Windows 10/Power‐ Shell, and Ubuntu Linux/Windows Subsystem for Linux (WSL). While I love how well Rust works on both Windows and Unix operating systems, two programs (findr and lsr) work slightly dif‐ ferently on Windows due to some fundamental differences in the operating system from Unix-type systems. I recommend that Win‐ dows/PowerShell users consider also installing WSL and working through the programs in that environment. xiv | Preface
📄 Page
17
All the code in this book has been formatted using rustfmt, which is a handy tool for making your code look pretty and readable. You can use cargo fmt to run it on all the source code in a project, or you can integrate it into your code editor to run on demand. For instance, I prefer to use the text editor vim, which I have configured to automatically run rustfmt every time I save my work. I find this makes it much eas‐ ier to read my code and find mistakes. I recommend you use Clippy, a linter for Rust code. Linting is automatically checking code for common mistakes, and it seems most languages offer one or more linters. Both rustfmt and clippy should be installed by default, but you can use rustup component add clippy if you need to install it. Then you can run cargo clippy to have it check the source code and make recommendations. No output from Clippy means that it has no suggestions. Now you’re ready to write some Rust! Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs to refer to program ele‐ ments such as variable or function names, databases, data types, environment variables, statements, and keywords. Constant width bold In blocks of code, unless stated otherwise, this style calls special attention to ele‐ ments being described in the surrounding discussion. In discursive text, it high‐ lights commands that can be used by the reader as they follow along. Constant width italic Shows text that should be replaced with user-supplied values or by values deter‐ mined by context. This element signifies a tip or suggestion. Preface | xv
📄 Page
18
This element signifies a general note. This element indicates a warning or caution. Using Code Examples Supplemental material (code examples, exercises, etc.) is available for download at https://oreil.ly/commandlinerust_code. If you have a technical question or a problem using the code examples, please send email to bookquestions@oreilly.com. This book is here to help you get your job done. In general, if example code is offered with this book, you may use it in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but generally do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “Command-Line Rust by Ken Youens-Clark (O’Reilly). Copyright 2022 Charles Kenneth Youens-Clark, 978-1-098-10943-1.” If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at permissions@oreilly.com. O’Reilly Online Learning For more than 40 years, O’Reilly Media has provided technol‐ ogy and business training, knowledge, and insight to help companies succeed. xvi | Preface
📄 Page
19
Our unique network of experts and innovators share their knowledge and expertise through books, articles, and our online learning platform. O’Reilly’s online learning platform gives you on-demand access to live training courses, in-depth learning paths, interactive coding environments, and a vast collection of text and video from O’Reilly and 200+ other publishers. For more information, visit http://oreilly.com. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-998-9938 (in the United States or Canada) 707-829-0515 (international or local) 707-829-0104 (fax) We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at https://oreil.ly/commandLineRust. Email bookquestions@oreilly.com to comment or ask technical questions about this book. For news and information about our books and courses, visit http://oreilly.com. Find us on Facebook: http://facebook.com/oreilly Follow us on Twitter: http://twitter.com/oreillymedia Watch us on YouTube: http://www.youtube.com/oreillymedia Acknowledgments My first debt of gratitude is to the Rust community for creating such an incredible language and body of resources for learning. When I started writing Rust, I quickly learned that I could try to write a naive program and just let the compiler tell me what to fix. I would blindly add or subtract & and * and clone and borrow until my program compiled, and then I’d figure out how to make it better. When I got stuck, I invariably found help at https://users.rust-lang.org. Everyone I’ve encountered in Rust, from Twitter to Reddit, has been kind and helpful. I would like to thank the BSD and GNU communities for the programs and docu‐ mentation upon which each chapter’s project is based. I appreciate the generous licenses that allow me to include portions of the help documentation from their programs: Preface | xvii
📄 Page
20
• https://www.freebsd.org/copyright/freebsd-license • https://creativecommons.org/licenses/by-nd/4.0 I further wish to thank my development editors, Corbin Collins and Rita Fernando, and my production editors, Caitlin Ghegan and Greg Hyman. I am deeply indebted to the technical reviewers Carol Nichols, Brad Fulton, Erik Nordin, and Jeremy Gai‐ lor, who kept me on the straight and narrow path, as well as others who gave of their time to make comments, including Joshua Lynch, Andrew Olson, Jasper Zanjani, and William Evans. I also owe thanks to my bosses over the last few years, Dr. Bonnie Hurwitz at the University of Arizona and Amanda Borens at the Critical Path Insti‐ tute, who have tolerated the time and effort I’ve spent learning new languages such as Rust in my professional job. In my personal life, I could not have written this book without the love and support of my wife, Lori Kindler, and our three extremely interesting children. Finally, I would also like to thank my friend Brian Castle, who tried so hard in high school to redirect my musical tastes from hard and progressive rock to alternative bands like Depeche Mode, The Smiths, and They Might Be Giants, only the last of which really took. xviii | Preface