1 Feelin’ Rusty

This chapter covers

This book will help beginner-to-intermediate Rust developers get up to speed on the language, tooling, data structures, memory management, testing, asynchronous programming, and best practices as quickly as possible. By the end of this book, you should feel confident building production-grade software systems with idiomatic—or Rustaceous—Rust. This book is not an exhaustive reference of the Rust language or its tooling; instead, it focuses solely on the good stuff.

Rust offers compelling features for those looking to build fast, safe programs. Some people find Rust’s learning curve a bit steep, and this book can help overcome the challenging parts, clarify Rust’s core concepts, and provide actionable advice.

The book is written for those already familiar with the Rust programming language. Additionally, it will be of much benefit to the reader to have experience with other system-level programming languages, such as C, C++, and Java. You need not be an expert in Rust to get value out of this book, but I won’t spend much time reviewing basic syntax, history, or programming concepts.

Many of the code samples in this book are partial listings, but the full working code samples can be found on GitHub at http://mng.bz/BA70. The code is made available under the MIT license, which permits usage, copying, and modifications without restriction. I recommend you follow along the full code listings if you can, to get the most out of this book. The code samples are organized by chapter within the repository; however, some examples may span multiple sections or chapters and are, thus, named based on their subject matter.

1.1 What’s Rust?

Rust (figure 1.1) is a modern programming language with a focus on performance and safety. It has all the features you would likely want or expect from a modern programming language, including closures, generics, asynchronous I/O, powerful tooling, IDE integrations, linters, and style-checking tools. Alongside Rust’s robust set of features, it boasts a vibrant, growing community of developers and contributors.

CH01_F01_Matthews

Figure 1.1 Rust language logo (Source: Rust core team. Licensed under CC BY 4.0.)

Rust is a powerful language with several applications, including web development. While it was written with the intention of being a systems-level language, it also fits quite well in domains that are well outside system-level programming, such as web programming with WebAssembly (Wasm), a web standard for executing bytecode. In figure 1.2, I’ve illustrated where Rust generally sits in the language stack, but this is by no means definitive.

CH01_F02_Matthews

Figure 1.2 Where Rust fits in language classifications

Rust’s creators envisioned its primary use as building system-level code and libraries that are safety and performance critical. Rust’s safety guarantees don’t come for free; the cost of those features comes in terms of added language and compilation-time complexity.

Rust can compete with higher-level languages, like Python or Ruby; however, its main drawback here is Rust’s lack of a runtime interpreter, as it is compiled to platform-dependent binaries. Thus, one must distribute their Rust programs as binaries (or somehow provide a compiler). There are a few cases in which Rust is likely a much better choice than a scripting language, like Python or Ruby, such as embedded or resource-constrained environments.

Rust can also be compiled for web browsers directly, through the use of Wasm, which has grown significantly in popularity recently. Wasm is simply treated as yet another CPU target, much like x86-64 or AArch64, except the CPU in this case is a web browser.

Some highlights of the Rust language include the following:

The most loved language

At the time of writing, Rust has won the most loved programming language category of Stack Overflow’s annual developer survey every year since 2016. In the 2021 survey, out of 82,914 responses, Rust was loved by 86.98% of those using it (“2021 Developer Survey,” http://mng.bz/ddON). The second-place language, Clojure, came in at 81.12% loved, and the third-place language, TypeScript, was 72.73% loved.

1.2 What’s unique about Rust?

Rust addresses common programming mistakes with a unique set of abstractions—some of which you may have never encountered before. In this section, I’ll provide a quick tour of the features that make Rust different.

1.2.1 Rust is safe

Safety is one of Rust’s hallmarks, and its safety features are what differentiates it most from other languages. Rust can provide strong safety guarantees thanks to a feature called the borrow checker.

In languages like C and C++, memory management is a somewhat manual process, and developers must be aware of the implementation details when considering memory management. Languages like Java, Go, and Python use automatic memory management, or garbage collection, which obfuscates the details of allocating and managing memory with the tradeoff of incurring some performance overhead.

Rust’s borrow checker works by validating references at compile time, rather than reference counting or performing garbage collection at run time. It’s a unique feature that also introduces challenges when writing software, especially if you’ve never encountered the borrow checker.

The borrow checker is part of Rust’s compiler rustc, which verifies that for any given object or variable, there can be no more than one mutable reference at a time. It’s possible to have multiple immutable references (i.e., read-only references) to objects or variables, but you can never have more than a single active mutable reference. As shown in figure 1.3, Rust guarantees memory safety by checking that there’s never an overlap between mutable and immutable references.

CH01_F03_Matthews

Figure 1.3 Rust borrow checker rules Venn diagram

Rust uses resource acquisition is initialization (RAII) to keep track of when variables and all their references are in and out of scope. Once they are out of scope, memory can be released. The borrow checker will not allow references to out-of-scope variables, and it only allows one mutable reference or multiple immutable references but never both.

The borrow checker provides safety for concurrent programming, too. Race conditions may arise when sharing data, such as between separate threads. In most cases, the root cause is the same: simultaneous shared mutable references. With Rust, it’s not possible to have more than one mutable reference, thereby ensuring data synchronization problems are avoided, or at least not created unintentionally.

Rust’s borrow checker is tricky to master at first, but soon, you’ll find it’s the best feature of Rust. Similar to languages like Haskell, once you manage to make your code compile, that’s often enough (when combined with adequate testing) to guarantee your code will work and never crash (testing is covered in chapters 6 and 7). There are exceptions to this, but by and large, code written in Rust will not crash from common memory errors, like reading past the end of a buffer or mishandling memory allocations and deallocations.

1.2.2 Rust is modern

The Rust language developers have paid special attention to supporting modern programming paradigms. Coming from other languages, you may notice Rust’s out-with-the-old, in-with-the-new approach. Rust largely eschews paradigms like object-oriented programming in favor of traits, generics, and functional programming.

Notably, Rust emphasizes the following paradigms and features:

Traditional object-oriented features are notably absent from Rust. And while it’s true that you can model patterns similar to classes and inheritance in Rust, the terminology is different, and Rust lends itself to functional programming. For those coming from an object-oriented background, such as C++, Java, or C#, it may take some time to get used to. Once they adjust to the new patterns, many programmers find a certain delight and freedom in being liberated from the rigidness of object-oriented ideology.

1.2.3 Rust is pure open source

When considering languages and platforms to build upon, community governance is important to consider when thinking about the long-term maintenance of any project. Some languages and platforms that are open source but mostly governed by large companies, such as Go (Google), Swift (Apple), and .NET (Microsoft), come with certain risks, such as making technology decisions that favor their products.

Rust is a community-driven project, led primarily by the not-for-profit Mozilla Foundation. The Rust programming language itself is dual licensed under the Apache and MIT licenses. Individual projects within the Rust ecosystem are individually licensed, but most key components and libraries exist under open source licenses, such as MIT or Apache.

There is strong support among large technology companies for Rust. Amazon, Facebook, Google, Apple, Microsoft, and others have made plans to use or pledge support for Rust. By not being tied to any particular entity, Rust is a good long-term choice with minimal potential for conflict of interest.

Note The Rust team maintains a list of production users on the official Rust language website: https://www.rust-lang.org/production.

1.2.4 Rust vs. other popular languages

Table 1.1, while not exhaustive, provides a summary of the differences between Rust and other popular programming languages.

Table 1.1 Rust compared to other languages

Language

Paradigms

Typing

Memory model

Key features

Rust

Concurrent, functional, generic, imperative

Static, strong

RAII, explicit

Safety, performance, async

C

Imperative

Static, weak

Explicit

Efficiency, portability, low-level memory management, widely supported

C++

Imperative, object-oriented, generic, functional

Static, mixed

RAII, explicit

Efficiency, portability, low-level memory management, widely supported

C#

Object-oriented, imperative, event-driven, functional, reflective, concurrent

Static, dynamic, strong

Garbagecollected

Supported on Microsoft platforms, large ecosystem, advanced language features

JavaScript

Prototypes, functional, imperative

Dynamic, duck, weak

Garbagecollected

Widely supported, async

Java

Generic, object-oriented, imperative, reflective

Static, strong

Garbagecollected

Bytecode-based, production-grade Java Virtual Machine, widely supported, large ecosystem

Python

Functional, imperative, object-oriented, reflective

Dynamic, duck, strong

Garbagecollected

Interpreted, highly portable, widely used

Ruby

Functional, imperative, object-oriented, reflective

Dynamic, duck, strong

Garbagecollected

Syntax, everything’s an expression, simple concurrency model

TypeScript

Functional, generic, imperative, object-oriented

Static, dynamic, duck, mixed

Garbagecollected

Types, JavaScript compatibility, async

1.3 When should you use Rust?

Rust is a systems programming language, generally meant to be used for lower-level system programming, in situations similar to where you’d use C or C++. Rust may not be well suited for use cases in which you want to optimize for developer productivity, as writing Rust is often trickier than writing code with popular languages like Go, Python, Ruby, or Elixir.

Rust is also a great candidate for web programming with the onset of Wasm. You can build applications and libraries with Rust, compile them for Wasm, and take advantage of the benefits of Rust’s safety model with the portability of the web.

There is no specific use case for Rust—you should use it where it makes sense. I have personally used Rust for many small one-off projects, simply because it’s a joy to write, and once the code compiles, you can generally count on it working. With proper use of the Rust compiler and tooling, your code is considerably less likely to have errors or behave in undefined ways—which are desirable properties for any project.

Tip Using the right tool for the job is important for any endeavor to succeed, but to know which tools are the right tools, you must first gain experience using a variety of different tools for different tasks.

1.3.1 Rust use cases

The following is a list of example use cases for which Rust is well suited:

1.4 Tools you’ll need

Included as part of this book is a collection of code samples, freely available under the MIT license. To obtain a copy of the code, you will need an internet-connected computer with a supported operating system and the tools discussed in table 1.2 installed. For details on installing these tools, refer to the appendix.

Table 1.2 Required tools

Name

Description

git

The source code for this book is stored in a public Git repository, hosted on GitHub at http://mng.bz/BA70.

rustup

This is Rust’s tool for managing Rust components. rustup will manage your installation of rustc and other Rust components.

gcc or clang

You must have a copy of GNU Compiler Collection (GCC) or Clang installed to build certain code samples, but it’s not required for most. Clang is likely the best choice for most people, and thus, it’s referred to by default. In cases where the clang command is specified, you may freely substitute gcc if you prefer.

Summary