I already know the ending it’s the part that makes your face implode
They Might Be Giants
Rust is reputed to have a fairly steep learning curve, but I’m convinced you can learn it much more quickly by writing many small programs you already know.
This book is about two things: systems programming and Rust.
Writing Rust versions of basic systems tools like
cal will reveal patterns that you’ll be able to use when you write your own programs—patterns like validating parameters, reading and writing file handles, parsing text, and using regular expressions.
Rust is a programming language created by Graydon Hoare while working at Mozilla Research. He first started working on it around 2006 as a personal project, and by 2010 Mozilla had sponsored and announced the project. While the language is relatively new, it has quickly earned a passionate following of programmers who claim to even love using it.
Often Rust is described as a systems programming language that has been designed for performance and safety.
It has a syntax that resembles the C language, so you’ll find things like
for loops, semicolon-terminated statements, and curly braces denoting block structures.
Rust is also a statically typed language, meaning that a variable can never change its type, like from a number to a string. If you’re coming from languages like C/C++ or Java, this will be familiar because those languages are also statically typed. You don’t always have to declare a variable’s type in Rust because the compiler can often figure it out from the context, but the variable is still never allowed to change its type.
In those languages, a variable can change its type at any point in the program, like from a string to a file handle.
The language may also silently treat a variable of one type like another; for instance, both Perl and Python will evaluate strings, numbers, lists, dictionaries, and more in a Boolean context which can lead to some surprising results.
Interestingly, several dynamically typed languages including Python, Perl 6/Raku, and Julia have introduced type hints or gradual typing systems where a variable can be basically untyped (
Any) or some specific type like an integer or string.
Although Rust bears a strong resemblance to imperative, C-style languages, it has borrowed many exciting concepts from other languages and programming paradigms.
Rust is not an object-oriented (OO) language like 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 ideas from purely functional languages like Haskell.
For instance, variables are immutable by default, meaning they can’t be changed.
Like many languages, functions are first-class values, which means they can be passed as arguments.
Most exciting to my mind is Rust’s use of enumerated and sum types, also called algebraic data types (ADTs), which allow you to represent, for instance, that a function can return a
Result which 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 forgetting to handle an error that could unexpectedly crash your program.
Rust requires learning some pretty low-level stuff about memory and types, so I can’t imagine it would be a great first language. I’m going to offload much of the nitty-gritty to reference books like Programming Rust (Blandy, Orendorff, and Tindall; O’Reilly, 2021) and The Rust Programming Language (Klabnik and Nichols; No Starch Press, 2019). I highly recommend that you read one or both of those along with this book to dig deeper into the language itself.
This book will focus on how to write practical programs, starting from scratch and working step-by-step to add features, build your program, work through error messages, and test your logic. You should read this book if you want to learn how to write complete programs that solve common systems problems. Although I’ll be showing why Rust is particularly well-suited for this, I think you should also try writing these programs in other languages you know so that you can contrast what makes Rust better or worse, easier or harder.
Lastly, you should also read this book if you care about testing. I’m an advocate for test-driven development where I write tests first and then try to write code that passes those tests. My experience teaching has convinced me that this is an effective way to think through problems. It’s my opinion that testing generally produces better code. If you aren’t currently using tests, I hope that you’ll see enough examples of their utility here to adopt this practice.
There are many reasons why you might be interested to learn Rust. Personally, Rust’s type system was very attractive to me. The more I used statically typed languages, the more I realized that dynamically typed languages force me, the programmer, to write correct programs and tests. The more I used Rust, the more I found that the compiler was my dance partner, 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.
One thing I like about Rust is how easily I can share a program I’ve written with someone who is not a developer. If I write a Python program for a workmate, I must give them the Python source code to run. This means that I must also ensure the user has installed 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 workmates a copy of the program. Assuming they have the correct architecture, they would not need to install Rust and could run the program directly.
The ease and size of distribution for Rust programs also applies to the containers I create. For instance, a Docker container with the Python runtime may require several hundred MB. In contrast, I can build a bare-bones Linux VM 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 would prefer to write in Rust and have smaller, leaner containers.
Mostly what I’ve found with Rust is that I’m extremely productive. For a long time, I’ve had a school-boy crush on languages like Lisp and Haskell, but I’ve never successfully written programs in those languages that do anything useful. To be clear, I generally write command-line programs that need to accept some arguments, read and write data to files or a database, fetch data from the internet, and so forth. Languages like Haskell would leave me overwhelmed with libraries that have inscrutable documentation and produce unintelligible error messages. Conversely, I can easily find many useful and well-documented Rust crates—which is what libraries are called in Rust—on crates.io, and working with the compiler feels like pair programming with an extremely smart coworker who finds errors and often suggests exactly how to fix them.
This is a book of coding challenges for you to write. I don’t want you to passively read this book on the bus to work and put it away. For each chapter, I want you to write a program that will pass the test suite I’ve written for you. 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 hail mostly from the Unix command line core utils because I expect these will already be quite familiar to the reader.
For instance, I assume you’ve used
tail to look at the first or last few lines of a file, but have you ever written your own versions of these programs?
There seems to be a desire in Rust to rewrite existing programs, especially ones originally written in C or C++, so that’s another part of my motivation.
You’ll be able to find many other examples of these programs written by Rustaceans on the web, so you can contrast your code with my solutions and the others you’ll find.
Beyond that, these are fairly simple programs that lend themselves to teaching a few skills in each program.
I’ve sequenced them so that they build upon each other, so it’s probably best if you work through all 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 programs, 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 Distribution) 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 challenge program, I use a shell script to redirect the output from the original programs into output files. The goal is then to have the Rust program 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 only try to implement a subset of the original programs 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 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.
To start, you’ll need to install Rust.
One of my favorite parts about Rust is the
rustup tool for installing, upgrading, and managing Rust.
It works equally well on Windows and Unix-type operating systems (OS) like Linux and macOS.
You will need to follow the 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.
rustup doc to read copious volumes of documentation.
You can check the version of the
rustc compiler with the following command:
$ rustc --version rustc 1.54.0 (a178d0322 2021-07-26)
All the code, data, and tests for the programs can be found in my GitHub repository. You can use the Git source code management tool (which you may need to install) to get a copy of the code. The following command will create a new directory on your computer called rust-sysprog with the contents of the repository:
$ git clone https://github.com/kyclark/rust-sysprog.git
You will not write your code in this directory. You should create a separate directory elsewhere for your projects, preferably in your own GitHub repository that you create for this purpose. You will copy each chapter’s tests directory into your project directory to test your code.
One of the first tools you will encounter is Cargo, Rust’s build tool, package manager, and test runner.
You can use it to compile and test the code for Chapter 1.
Change into the directory and run the tests with
$ cd 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/Powershell, and Ubuntu Linux/Windows Subsystem for Linux (WSL). While I love how well Rust works on both Windows and Unix operating systems, two programs (
lsr) will not pass the entire test suite on Windows due to some fundamental differences in the operating system from Unix-type systems. I recommend Windows/Powershell users consider also installing WSL and working through the programs in that environment.
All the code in this book has been formatted using
rustfmt, which is a really 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 easier 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.
clippy should be installed by default, but you can use
rustup component add clippy if you need to install Clippy.
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!
The following typographical conventions are used in this book:
Indicates new terms, URLs, email addresses, filenames, and file extensions.
Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords.
Constant width bold
Shows commands or other text that should be typed literally by the user.
Constant width italic
Shows text that should be replaced with user-supplied values or by values determined by context.
This element signifies a tip or suggestion.
This element signifies a general note.
This element indicates a warning or caution.
Supplemental material (code examples, exercises, etc.) is available for download at https://github.com/oreillymedia/title_title.
If you have a technical question or a problem using the code examples, please send email to [email protected].
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: “Systems Programming with Rust by Ken Youens-Clark (O’Reilly). Copyright 2021 Charles Kenneth Youens-Clark, 978-0-596-xxxx-x.”
If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at [email protected].
For more than 40 years, O’Reilly Media has provided technology and business training, knowledge, and insight to help companies succeed.
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.
Please address comments and questions concerning this book to the publisher:
We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at http://www.oreilly.com/catalog/9781098109424.
Email [email protected] 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
I would like to thank my development editor, Corbin Collins, my production editor, <name here>. I am deeply indebted to the technical reviewers Carol Nichols, Brad Fulton, Erik Nordin, and Jeremy Gailor, as well as others who gave of their time to make comments including Joshua Lynch, Andrew Olson, and Jasper Zanjani.