Work In Progress

This book is still going through heavy edits. Pardon the dust.

What you will find here

This book is a compendium of Rust ecosystem examples and resources. It is intended to be everything you need for day-to-day Rust coding, in one place. It demonstrates good practices to accomplish common programming tasks, using the crates of the Rust ecosystem. It summarizes the language and key features of the standard library. It includes numerous links to Rust resources.

Who should read this book

This book is intended for

  • new Rust programmers, to get an overview of the capabilities of the Rust ecosystem and pointers to other resources,
  • experienced programmers, to find code examples and review best practices for common programming tasks.

Readers should have already some basic familiarity with Rust⮳ concepts. The Rust book⮳ is an excellent resource for complete beginners to get started with. This said, key features of the language are succinctly summarized in this book's language section.

Why this book

Per the curated list of Rust crates blessed.rs⮳, "the standard library in Rust is much smaller than in Python or Go, for example. Those languages come with "batteries included" support ... Rust, on the other hand, gets things like that from the crates.io ecosystem and the Cargo package manager. But with more than 160 thousand crates (libraries) to choose from, a common complaint from new Rust developers is that they don't know where to start, which crates they ought to use, and which crates they ought to trust." There are no dominant frameworks or platforms akin to Rails, Django, Spring or Node in the Rust world at this time.

This book therefore intends to provide EXAMPLES to demonstrate the uses of KEY CRATES, that is libraries necessary for day-to-day Rust coding - examples which are absent from or scattered in the reference documentation⮳. It hopes to become a "cheat sheet on steroid" for the Rust ecosystem (not just for the Rust language).

This book includes most of the "Rust Cookbook"

The "Rust How-to" project started as a set of notes kept while the author was learning Rust and evolved in a standalone book. The author then came across the Rust Cookbook⮳ community project, which shares very similar goals. Unfortunately, no updates have been made to that book in more than 4 years. Many of its examples no longer work. Several crates it references are no longer maintained. The author thus decided to merge the contents of the Rust Cookbook into this book, testing and refreshing its examples, and expanding its coverage significantly.

How to read this book

The left sidebar is organized by topic.

  • The book first quickly summarizes the basics of the language and often-used elements of the standard library.
  • The crates section provides pointers on how to locate key crates and provides alphabetical and categorical indices of crates used in the book.
  • The bulk of the book is divided in sections named after the crates.io categories⮳ whenever possible.
  • Each section contains a list of recipes. The recipes are simple statements of a task to accomplish, like "generate random numbers in a range"; and each recipe is tagged with badges indicating which crates they use, like rand, and which categories on crates.io those crates belong to, like cat-algorithms.
  • The book focuses on cross-cutting concerns that affect most aspects of development e.g. error handling, error customization, configuration, debugging...
  • Concurrency, including asynchronous programming, is covered in details. So are development tools.
  • programming domains such as CLI and Web development.

The links section provides pointers to notable Rust websites, learning resources, cheat sheets, books, and code examples...

The contributing section details how to contribute to the book itself.

New Rust programmers should be comfortable reading from the first section to the last, and doing so should give one a strong overview of the crate ecosystem. Click on a topic in the sidebar to navigate to the page for that section of the book.

If you are simply looking for the solution to a simple task, the easiest ways to find a specific recipe are to

  • use the search button,
  • scan the left-side bar for categories you are interested in,
  • scan the Index of examples, and from there, click on the name of the recipe to view it.
  • look up into the Word index lists concepts, crates (in lowercase), and Rust items (using their full path e.g. parking_lot::ReentrantMutex).
  • consult the alphabetical and categorical crates indices.

How to use the recipes

Recipes are designed to give you instant access to working code, along with a full explanation of what it is doing, and to guide you to further information. All recipes are self-contained programs, so that they may be copied directly into your own projects for experimentation. To do so follow the instructions below.

Consider this example for "generate random numbers within a range":

rand cat-algorithms

use rand::Rng;
fn main() {
    let mut rng = rand::thread_rng();
    println!("Random f64: {}", rng.r#gen::<f64>());
}

To work with it locally we can run the following commands to create a new cargo project, and change to that directory:

cargo new my-example --bin
cd my-example

Now, we also need to add the necessary crates to Cargo.toml⮳, as indicated by the crate badges, in this case just "rand". To do so, we'll use the cargo add command.

cargo add rand

Next you can replace src/main.rs with the full contents of the example and run it:

cargo run

The crate badges that accompany the examples link to the crates' full documentation on docs.rs⮳, and is often the next documentation you should read after deciding which crate suites your purpose.

A note about error handling

Error handling in Rust is robust when done correctly, but can require a fair bit of boilerplate. Because of this, one often sees Rust examples filled with unwrap calls, instead of proper error handling.

Since this book's recipes are intended to be reused as-is and encourage best practices, they set up error handling correctly when there are Result types involved. The structure generally looks like:

use std::net::IpAddr;
use std::str;

use anyhow::Result;

fn main() -> Result<()> {
    let bytes = b"2001:db8::1";

    // Bytes to string.
    let s = str::from_utf8(bytes)?;

    // String to IP address.
    let addr: IpAddr = s.parse()?;

    println!("{:?}", addr);
    Ok(())
}
use anyhow::Result;
use url::Position;
use url::Url;

fn main() -> Result<()> {
    let parsed = Url::parse("https://httpbin.org/cookies/set?k2=v2&k1=v1")?;
    let cleaned: &str = &parsed[..Position::AfterPath];
    println!("cleaned: {}", cleaned);
    Ok(())
}

In most examples, we have chosen to use anyhow's Result as the return type of any fallible function, instead of writing std::result::Result<_, Box<dyn std::error::Error>> or using custom Result / Error types.

Within the code, we use the ? operator to easily propagate any error that implements the std::error::Error trait.

For more background on error handling in Rust, read this page⮳ of the Rust book.

Additional examples

The crates/xmpl folder in the book's GitHub repo contains additional examples that can't be embedded into the book, due to their length.

A note about crate representation

This book is intended to provide expansive coverage of "key" or "foundational" crates - those crates that make up the most common programming tasks, and that the rest of the ecosystem builds off of.

Key crates are identified by cross-referencing:

The selection process is necessarily opinionated. Feel free to offer suggestions (or submit a PR), if the author missed an important, widely used crate.

What other books should I consult?

Rust by Example⮳ is similar in concept - a collection of runnable examples - but not in scope, as it focuses solely on the Rust language and standard library.

Consult the links section and its books page for other recommendations.

Index of Examples

Algorithms

Randomness

Sorting

Asynchronous

Async

Async and Blocking

Async Channels

Async Traits

RecipeCratesCategories
Async traitsasync-traitcat-asynchronous

Futures

Streams

RecipeCratesCategories
Streamsfuturescat-asynchronous

Tokio

RecipeCratesCategories
Basicstokiocat-asynchronous
Jointokiocat-asynchronous
Spawningtokiocat-asynchronous
IOtokiocat-asynchronous
Graceful shutdowntokio_graceful_shutdowncat-asynchronous

Authentication

Basic Authentication

RecipeCratesCategories
Perform a basic HTTP authenticationreqwestcat-authentication cat-network-programming

Command Line Interface

ANSI Terminal

Arguments

TUI

RecipeCratesCategories
Build complex TUIratatuicat-command-line-interface

User Interaction

Command Line Utilities

Filesystem

RecipeCratesCategories
lsdlsdcat-command-line-utilities
exaexacat-command-line-utilities
brootbrootcat-command-line-utilities
batbatcat-command-line-utilities
openopencat-command-line-utilities

Networking

RecipeCratesCategories
gpinggpingcat-command-line-utilities

Shells

RecipeCratesCategories
starshipstarshipcat-command-line-utilities
nushellnushellcat-command-line-utilities

Compression

tar

Concurrency

Concurrent Data Structures

Crossbeam

Data Parallelism

Explicit Threads

RecipeCratesCategories
Use spawn, joinstd[cat-concurrency]
Use scoped threadsstdcat-concurrency

Message Passing

Send

RecipeCratesCategories
Send and Sync traitsstdcat-concurrency

Shared State

Threadpool

Config

Configuration

RecipeCratesCategories
configconfigcat-config
confyconfycat-config

Environment Variables

RecipeCratesCategories
dotenvydotenvycat-config
std::envstdcat-config
envyenvycat-config

Cryptography

Encryption

RecipeCratesCategories
Salt and hash a password with PBKDF2ring data-encodingcat-cryptography

Hashing

Data Structures

Bitfield

Maps

Stack Allocated Arrays

RecipeCratesCategories
arrayvecarrayveccat-data-structures
smallvecsmallveccat-data-structures
tinyvectinyveccat-data-structures

UUID

RecipeCratesCategories
Generate and parse UUIDsuuidcat-data-structures

Database

Connection Pool

RecipeCratesCategories
Create a connection pooldeadpoolcat-database

NoSQL

RecipeCratesCategories
Connect to MongoDBmongodbcat-asynchronous cat-database cat-web-programming
Connect to Redisrediscat-database

Postgres

Query Builders Orms

RecipeCratesCategories
sqlxsqlxcat-database
dieseldieselcat-database
SeaORMsea-ormcat-database
toastytoastycat-database

SQLite

Date and Time

Duration

Parse

Time

RecipeCratesCategories
Use the time cratetimecat-date-and-time cat-value-formatting cat-parser-implementations cat-no-std

Cargo

Cargo

Crate Registries

RecipeCratesCategories
Crate registriescrates.iocat-development-tools

Package Layout

RecipeCratesCategories
Package layoutcargocat-development-tools

Documentation

Badges

RecipeCratesCategories
Badgesshield.iocat-development-tools

Documentation

mdBook

Formatting

Formatting

Installation

Install

Rustup

Other

Code Build

Code Verification

RecipeCratesCategories
Verify your Rust codekani{{hi:kani}}cat-development-tools

miri

Other

Versioning

Versioning

Development Tools: Build Utils

Build Utils

Development Tools: Cargo Plugins

Auditing

Building

Code Formatting Linting

Cross Compiling

Maintaining

Performance

Watching For Changes

RecipeCratesCategories
cargo watchcargo-watchcat-development-tools::cargo-plugins
cargo limitcargo-limitcat-development-tools::cargo-plugins

Writing

Development Tools: Debugging

Alternatives

Config Log

Diagnostic Functions

Log

Tracing

Encoding

Binary Encoders

Complex

CSV

Serde

RecipeCratesCategories
Serialize JSONserde_jsoncat-encoding
monostatemonostatecat-encoding
serde-ignoredserde-ignoredcat-encoding

Strings

Typecasts

RecipeCratesCategories
bytemuckbytemuckcat-encoding
zerocopyzerocopycat-encoding

Filesystem

cwd

RecipeCratesCategories
Get the current working directorystdcat-filesystem

Dir

File Watching

Ignore

Read-Write

Tempfile

User Directories

RecipeCratesCategories
dirsdirscat-filesystem
directoriesdirectoriescat-filesystem

Hardware Support

Processor

Mathematics

Additional Numeric Types

Complex Numbers

Linear Algebra

Statistics

Trigonometry

Memory Management

Global Static

RecipeCratesCategories
Declare lazily evaluated constantslazy_staticcat-caching cat-rust-patterns cat-memory-management

Lazy Initialization

RecipeCratesCategories
stdstdcat-memory-management
once_cellstd once_cellcat-memory-management
lazy_staticlazy_staticcat-memory-management

OS

External

Low Level System Calls

RecipeCratesCategories
Call libc, the C standard librarylibccat-os

Rust OS

Rust Patterns

Builder Pattern

RecipeCratesCategories
bonboncat-rust-patterns
derive_builderderive_buildercat-rust-patterns
typed-buildertyped-buildercat-rust-patterns

Design Patterns

Error Customization

RecipeCratesCategories
anyhowanyhowcat-rust-patterns
thisErrorthiserrorcat-rust-patterns
miettemiettecat-rust-patterns
color-eyrecolor-eyrecat-rust-patterns

Error Handling

Functional Programming

RecipeCratesCategories
Compose iteratorsitertoolscat-rust-patterns

Rust Idioms

RecipeCratesCategories
Rust idioms and patterns{{#crate }}cat-rust-patterns

Template Engine

Tera

RecipeCratesCategories
Create HTML files from a templateteracat-template-engine

Tinytemplate

Text Editors

IDEs

Text Processing

Regex

Regex2

RecipeCratesCategories
Longer Regex Exampleregexcat-text-processing

String Concat

RecipeCratesCategories
Compare string concatenation methodsstdcat-text-processing

String Parsing

Web Programming

Mime

Scraping

Url

Web Programming: HTTP Client

APIs

Download

HTTP Clients

RecipeCratesCategories
reqwestreqwestcat-web-programming::http-client
urequreqcat-web-programming::http-client
hyperhypercat-web-programming::http-client

Requests

Web Programming: HTTP Server

actix

RecipeCratesCategories
Create a web server with Actix Webactix-webcat-web-programming cat-web-programming::http-server

axum

RecipeCratesCategories
Create a web server with axumaxumcat-web-programming cat-web-programming::http-server

Batteries-Included Frameworks

RecipeCratesCategories
locoloco_rscat-web-programming::http-server
Rust on NailsRust on Nailscat-web-programming::http-server

CORS

RecipeCratesCategories
Implement CORStower tower-httpcat-web-programming

GraphQL

RecipeCratesCategories
Create a GraphQL endpointasync-graphqlcat-web-programming::http-server

gRPC

RecipeCratesCategories
Implement gRPCtoniccat-web-programming::http-server

hyper

RecipeCratesCategories
Implement an HTTP API with hyperhypercat-web-programming::http-server

Middleware

Other Frameworks

Static Website Generators

Contributing

API Documentation

Development Editing

Dev Container Docker

Dev Environment Setup

Optional Preprocessors

Publication

Repo Structure

Topics of Interest

Crates

Language

Attributes

Closures

Control Flow

Enums

Functions

Generics

Iterators

Lifetimes

Macros

Main

Match

Modules

Ownership Borrowing

Rust Install

Simple Data Types

Slices

Structs

Traits

Trait Objects

Variables and Constants

Blogs Podcasts Meetups

Books

Companies

Example Code

Learning

Rust Cheatsheets

Standard Library

AsRef

RecipeCrates
AsRef and &Tstd

Cow

Derive

Hashmaps

RecipeCrates
Hashmapsstd

Option

Result

RecipeCrates
Resultstd

Smart Pointers

RecipeCrates
Boxstd
Rcstd
RefCellstd

Strings

Vectors

RecipeCrates
Vecstd

Rust language

Rust is a modern programming language that offers high performance, reliability, and productivity. It is designed to prevent common errors such as memory leaks, data races, and null pointer dereferences, by enforcing strict rules at compile time. Rust also supports powerful features such as generics, traits, macros, and concurrency, making it suitable for a wide range of applications.

Rust prefers snake case for variables and functions, so a method would be called read_str instead of readStr. For structs, traits and enums, camel case (or Pascal case) is used, for example HttpClient.

Rust Installation

Install Rust

Install Rust and create a first project

On WSL / Unix:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • Check whether you have Rust installed correctly
rustc --version
cargo --version
  • Open the documentation, if needed
rustup doc
  • Create a new project
cargo new hello_world
cd hello_world
code . # open VS Code (or your favorite editor) and edit the code as you wish
  • Build / run the code.
cargo check # check if the code can compile
cargo build # compile
cargo run # run the executable

cargo run builds the code if cargo build has not been invoked before or the code has changed.

Main function

use std::fs::File;
use std::io::Read;

use anyhow::Result;
use anyhow::anyhow;

fn read_uptime() -> Result<u64> {
    let mut uptime = String::new();
    File::open("/proc/uptime")?.read_to_string(&mut uptime)?;

    Ok(uptime
        .split('.')
        .next()
        .ok_or(anyhow!("Cannot parse uptime data"))?
        .parse()?)
}

fn main() {
    match read_uptime() {
        Ok(uptime) => println!("uptime: {} seconds", uptime),
        Err(err) => eprintln!("error: {}", err),
    };
}

Async main function

use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    println!("I'm async!");
    Ok(())
}

Simple data types

Rust by example - Primitives

  • Integers: i8⮳, i16⮳, i32⮳, i64⮳, i128⮳, isize
  • Unsigned: u8⮳, u16⮳, u32⮳, u128⮳, usize
    • usize⮳ and isize⮳ are 32 or 64 bits, depending on the architecture of the computer.
  • Floating point: f32⮳, f64
  • Boolean: bool⮳: true, false
  • Char: let z: char = 'ℤ'; Unicode
  • Tuples: let tup: (i32, f64, u8) = (500, 6.4, 1);
    • Access via let five_hundred = x.0;
    • Destructuring via let (x, y, z) = tup;
  • Arrays: let a: [i32; 5] = [1, 2, 3, 4, 5]; allocated on the stack. access via let first = a[0];
    • A vector is a similar collection type provided by the standard library that is allowed to grow or shrink in size
  • Unit (aka void): ()
  • Type aliases: type Kilometers = i32;

Handle overflows

  • Wrap in all modes with the wrapping_* methods, such as wrapping_add⮳.
  • Return the std::option::Option::None⮳ value if there is overflow with the checked_* methods.
  • Return the value and a boolean indicating whether there was overflow with the overflowing_* methods.
  • Saturate at the value’s minimum or maximum values with the saturating_* methods.

Variables and Constants

Rust by example - Variable bindings Rust by example - constants


fn main() {
    // `const` is set to a constant expression
    // The type must be annotated
    const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
    // ERROR: THREE_HOURS_IN_SECONDS = 10800;

    // Immutable variable
    let apples = 5;
    // ERROR: apples = 6;
    println!("apples: {}", apples);

    // Mutable variable
    let mut guess = String::new();
    guess.push_str("42");
    println!("guess: {}", guess);
}

Shadowing

fn main() {
    // `x` is an immutable variable
    let x = 5;
    // ERROR: x = x +1;

    // But it can be redefined:
    let x = x + 1;
    println!("{x}");

    // The type can change
    let x = "example";
    println!("{x}");
}

Destructuring

fn main() {
    // Destructuring a tuple
    let (x, y, _) = (1, 2, 3);
    // x, y are now stored individually in two separate `i32` variables
    // Use _ to ignore a field you don't care about.
    println!("x: {x}, y: {y}");

    struct Point {
        x: i32,
        y: i32,
    }

    let p = Point { x: 0, y: 7 };
    // Destructuring a struct - sets a = 0 and b = 7:
    let Point { x: a, y: b } = p;
    println!("a: {a}, b: {b}");

    // Here is a simpler way:
    let Point { x, y } = p;
    // This is equivalent to `let Point { x: x, y: y } = p;``
    print!("x and y: {:?}", (x, y));
    // An underscore can be used as well, if needed.
}

Starting the name of a variable with an underscore silences unused variable warnings.

Ownership and Borrowing

Ownership

Rust by example - Ownership

  • No garbage collector. Ownership instead.
  • Each value in Rust has an owner.
  • There can only be one owner at a time.
fn main() {
    // Strings have move semantics.
    let s1 = String::from("hello");
    // s1 is MOVED into s2 - this is NOT a shallow copy
    let s2 = s1;
    println!("{}, world!", s2);
    // ...but Rust has invalidated s1
    // ERROR: println!("{}, world!", s1);
}

When the owner goes out of scope, the value will be dropped.

fn main() {
    {
        let s = String::from("hello");
        println!("{}", s);
    } // The `s` variable is now out of scope - Rust calls `drop`

    // ERROR println!("{}", s);
}

Rust will never automatically create “deep” copies of your data. Use std::clone::Clone

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();
    // `clone` deeply copies the heap data of the `String`,
    // not just the stack data.
    println!("{s2}");
}

If a type implements the std::marker::Copy⮳ trait (stack-only, fixed-size values, like integers, floats, and tuples thereof), variables that use it do not move, but rather are trivially copied, making them still valid after assignment to another variable.

fn main() {
    let x = 5; // Integer
    let y = x; // No MOVE

    println!("x = {}, y = {}", x, y); // OK
}

Borrowing

Passing a variable to a function will move or copy, just as assignment does. To avoid passing a value along, borrow the value:

fn main() {
    let s1 = String::from("hello");

    let _len = calculate_length(&s1); // `&s1` passes an immutable reference to `s1`

    fn calculate_length(s: &str) -> usize {
        s.len()
    } // Here, `s` goes out of scope. Because the function does not have
    // ownership of what it refers to, `s1` is not dropped.

    println!("{s1}");
}

Mutable references


// Note the `&mut` in the function's signature.
fn change(some_string: &mut String) {
    some_string.push_str(", world");
    println!("{some_string}");
}

fn main() {
    let mut s = String::from("hello"); // note the `mut`
    change(&mut s);
}

If you have a mutable reference to a value, you can have no other simultaneous references to that value! Functions like a read/write lock.

Slices

Slices

fn main() {
    let s = String::from("hello world");

    let hello: &str = &s[0..5]; // or &s[..5];
    let world = &s[6..11]; // or &s[6..];

    println!("{}", hello);
    println!("{}", world);
}

Functions

Rust by example - Functions

fn foo(x: i32, unit_label: char) -> i32 {
    let y = {
        let z = 3;
        x + z // Expression at the end of a block - no semi-colon
    };

    println!("The value of y is: {y}{unit_label}");
    y // Returns y - no semi-colon
}

fn main() {
    println!("{}", foo(1, 'm'));
}

The unit type () (void in some languages) is the default return type when no type is given for a function. It could be omitted: fn log(message: &str) { ... }

Generic functions

fn generic<T>(_t: T) {
    println!("In `generic`");
}

// Explicitly specified type parameter `char` to `generic()`.
// Note the turbofish notation ::<>

fn main() {
    generic::<char>('a');
}
use std::fmt::Display;

fn generic<T: ?Sized + Display>(t: &T) {
    // By default, generic functions will work only on types that have a
    // known size at compile time. Use `?Sized` to relax that rule.
    // `t` must be some kind of (smart) pointer: &, `Rc`, `Box`...
    println!("{}", t);
}

fn main() {
    let s = String::from("hello");
    generic(&s[..]);
}

Function pointers

fn add_one(x: i32) -> i32 {
    x + 1
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    // function pointer
    f(arg) + f(arg)
}

fn main() {
    println!("{}", do_twice(add_one, 1));
}

Diverging functions

Diverging functions never return.

fn foo() -> ! {
    // ! is the Never type
    panic!("This call never returns.");
}

fn main() {
    println!("Will panic");
    foo();
}

Control flow

Rust by example - Control flow

If else

fn main() {
    let number = 3;
    let result: u8 = if number < 5 {
        println!("Condition was true");
        5 // `if` is an expression
    } else {
        println!("Condition was false");
        6
    };
    println!("{}", result);
}

Also else if <cond> { ... }

Loop

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
            // `continue` and loop labels also exist:
            // https://doc.rust-lang.org/book/ch03-05-control-flow.html
        }
    };
    println!("{}", result);
}

While

fn main() {
    let mut number = 5;
    while number != 0 {
        println!("{number}!");
        number -= 1;
    }
}

For

fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a {
        println!("the value is: {element}");
    }

    // Range - generates all numbers in sequence
    // starting from one number and ending before another number.
    for number in (1..4).rev() {
        // reverse enumeration
        println!("{number}!");
    }
}

Structs

Rust by example - Structs


// We first define the struct's fields (which can be of any type).
// The `derive` attribute is not required - it just enables `println!` below.
#[derive(Debug)]
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

fn main() {
    // We create an instance of the struct.
    // Note that there is no `new` or similar.
    let user1 = User {
        active: true,
        username: String::from("someusername123"),
        email: String::from("someone@example.com"),
        sign_in_count: 1,
    };
    println!("{:?}", user1);
}

Struct fields follow the general rule of everything being private by default unless annotated with pub⮳.


#[derive(Debug)]
struct User {
    active: bool,
    username: String,
    email: String,
}

// It is common to define a function (or an associated function, see below) that
// initializes the struct:
fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username, /* Field init shorthand, instead of writing `username:
                   * username` */
        email, // Same
    }
}

fn main() {
    // We create an instance of the struct:
    let user1: User = build_user("user@example.com".into(), "user".to_string());

    // Then update the struct.
    // .. is used to fill in the rest
    let user2 = User {
        email: String::from("another@example.com"),
        ..user1 /* The remaining fields not explicitly set will have the
                 * same value as the fields in the given instance. */
    };
    println!("{:?}", user2);
}

// A tuple struct
// Note the ( ) and the lack of field names.
#[derive(Debug)]
struct Color(i32, i32, i32);

// A unit-like struct
#[derive(Debug)]
struct AlwaysEqual; // Note that there are no fields

fn main() {
    let black = Color(0, 0, 0);
    println!("{black:?}");
    let s = AlwaysEqual;
    println!("{s:?}");
}
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // implementation block (multiple allowed for a given struct)
    // Method
    fn area(&self) -> u32 {
        // short for self: &Self, an alias for the type that the impl block is
        // for
        self.width * self.height
    }

    // Associated Functions - NO self, &self, or &mut self
    // often use for constructors: SomeType::new(...)
    fn square(size: u32) -> Self {
        Self {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let sq = Rectangle::square(5);
    println!("Area: {}", sq.area());
}

Enums

Rust by example - Enums


#[derive(Debug)]
enum Message {
    Quit,                       // Unit-like variant (no fields)
    Move { x: i32, y: i32 },    // Struct-like variant (named fields)
    Write(String),              // Tuple-like variant (numbered fields)
    ChangeColor(i32, i32, i32), // Another tuple-like variant
}

// Define methods on enums.
impl Message {
    fn call(&self) {
        // method body would be defined here
    }
}

fn main() {
    // `msg` is assigned one of the variants.
    // Note the :: between the name of the type and the name of the variant.
    let msg = Message::Quit;
    println!("{msg:?}");
    // or
    let msg = Message::Move { x: 10, y: 15 };
    println!("{msg:?}");
    // or
    let msg = Message::ChangeColor(127, 0, 0);
    println!("{msg:?}");
}

If we make an enum public, all of its variants are then public. We only need pub⮳ before the enum⮳ keyword.

Traits


pub trait Summary {
    fn summarize(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

// Implement Trait on a Type
impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

fn main() {
    let na = NewsArticle {
        headline: "headline".to_string(),
        location: "location".to_string(),
        author: "author".to_string(),
        content: "...".to_string(),
    };
    println!("Summary: {}", na.summarize());
}

Trait methods are in scope only when their trait is.

Default implementation


trait Summary {
    fn summarize_author(&self) -> String;

    // Default implementation
    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
        // The default implementation can call a non-default
        // (abstract) method
    }
}

struct Blog {
    author: String,
}

impl Summary for Blog {
    fn summarize_author(&self) -> String {
        self.author.clone()
    }
}

fn main() {
    let blog = Blog {
        author: "ferris".into(),
    };
    println!("{}", blog.summarize());
}

Supertraits

use std::fmt;

trait OutlinePrint: fmt::Display {
    fn outline_print(&self) {
        println!("* {} *", self);
        // We can use `println!` here,
        // because `self` is guaranteed to implement `Display`
    }
}

// String implements Display. That would not work otherwise.
impl OutlinePrint for String {}

fn main() {
    String::from("test").outline_print();
}

"Newtype" pattern

Unlike interfaces in languages like Java, C# or Scala, new traits can be implemented for existing types.

trait MyHash {
    fn myhash(&self) -> u64;
}

impl MyHash for i64 {
    fn myhash(&self) -> u64 {
        *self as u64
    }
}

fn main() {
    let x = 1i64;
    println!("{}", x.myhash());
}

One restriction to note is that we can implement a trait on a type only if at least one of the trait or the type is local to our crate. If neither are, use the newtype pattern:

use std::fmt;

// Tuple struct wrapping the type we want to add a non-local trait to.
struct Wrapper(Vec<String>);

impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}
// If you want the new type to have every method the inner type has,
// implement the `Deref` trait instead.

fn main() {
    println!(
        "{}",
        Wrapper(vec!["example".to_string(), "example 2".to_string()])
    );
}

Traits as parameters

// Accepts any type that implements the specified trait:
fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}

// Trait bound syntax (mostly equivalent):
fn notify2<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

trait Summary {
    fn summarize(&self) -> String;
}

struct Article {
    txt: String,
}

impl Summary for Article {
    fn summarize(&self) -> String {
        self.txt.clone()
    }
}

fn main() {
    let a = Article {
        txt: String::from("Some text"),
    };
    notify(&a);
    notify2(&a);
}

Multiple traits


use std::clone::Clone;
use std::fmt::Debug;

// Note the `+`
fn a_function(item: &(impl Debug + Clone)) {
    println!("{:?}", item.clone());
}

fn some_function<T, U>(_t: &T, _u: &U) -> i32
where
    T: Debug + Clone, // Note the `+`
    U: Debug + Clone,
{
    42
}

#[derive(Debug, Clone)]
struct S;

fn main() {
    let s = S;
    a_function(&s);
}

Return-position impl Trait

fn returns_closure() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

fn main() {
    let f = returns_closure();
    println!("{}", f(1));
}

Generic traits

trait Test<T> {
    fn test(_t: T);
}

struct SomeStruct;

// Note the <> in two places:
impl<T> Test<T> for SomeStruct {
    fn test(_t: T) {
        println!("test");
    }
}

fn main() {
    SomeStruct::test(1);
    SomeStruct::test(true);
}

Associated types


trait Iterator {
    type Item; // <-- associated type

    // Note the use of :: to refer to the associated type
    fn next(&mut self) -> Option<Self::Item>;
}

struct MyIterator(u32);

// We implement the trait for a given struct
impl Iterator for MyIterator {
    // ...and define what associated type should be used here
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        Some(self.0)
    }
}

fn use_iterator(it: &mut impl Iterator<Item = u32>) -> Option<u32> {
    it.next()
}

// A common pattern is a generic type (with a default) and an associated type:
trait Add<Rhs = Self> {
    type Output; // <-- associated type

    fn add(self, rhs: Rhs) -> Self::Output;
}

fn main() {
    let mut it = MyIterator(42);
    println!("{:?}", use_iterator(&mut it));
}

Trait bounds

use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;

// Trait bounds: the `print_hash` function is generic over an unknown
// type `T`, but requires that `T` implements the `Hash` trait.
fn print_hash<T: Hash>(t: &T) {
    let mut hasher = DefaultHasher::new();
    t.hash(&mut hasher);
    println!("The hash is {:x}", hasher.finish());
}
struct Pair<A, B> {
    first: A,
    second: B,
}

// Generics make it possible to implement a trait conditionally.
// Here, the Pair type implements Hash if, and only if,
// its components do.
impl<A: Hash, B: Hash> Hash for Pair<A, B> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.first.hash(state);
        self.second.hash(state);
    }
}

fn main() {
    let p = Pair {
        first: 1,
        second: "2",
    };
    print_hash(&p);
}

Constants in traits

trait Example {
    const CONST_NO_DEFAULT: i32;
    const CONST_WITH_DEFAULT: i32 = 99;
}
struct S;

impl Example for S {
    const CONST_NO_DEFAULT: i32 = 0;
}

fn main() {
    println!("{} {}", S::CONST_NO_DEFAULT, S::CONST_WITH_DEFAULT);
}

Async and traits

See Async

See also

Traits (blog)

Trait objects

In Rust, traits are types, but they are "unsized", which roughly means that they are only allowed to show up behind a pointer like std::boxed::Box⮳ (which points onto the heap) or & (which can point anywhere).

A type like &dyn ClickCallback or Box<dyn ClickCallback> where ClickCallback is a Trait, is called a "trait object", and includes a pointer to an instance of a type T implementing ClickCallback, and a 'vtable': a pointer to T's implementation of each method in the trait.

trait Draw {
    fn draw(&self);
}

struct Button;

impl Draw for Button {
    fn draw(&self) {
        println!("Button");
    }
}

struct Text;

impl Draw for Text {
    fn draw(&self) {
        println!("Text");
    }
}

struct Screen {
    components: Vec<Box<dyn Draw>>, // <-- trait object
}

impl Screen {
    fn new() -> Self {
        Screen {
            components: vec![Box::new(Button), Box::new(Text), Box::new(Text)],
        }
    }

    fn run(&self) {
        for component in self.components.iter() {
            // The purpose of trait objects is to permit "late binding" of
            // methods. Calling a method on a trait object results
            // in virtual dispatch at runtime. Here, `components` is
            // a mix of `Button` and `Text` structs.
            component.draw();
        }
    }
}

fn main() {
    let s = Screen::new();
    s.run();
}

The set of traits after dyn can be made up of an object-safe⮳ base trait plus any number of autotraits (one of std::marker::Send⮳, std::marker::Sync⮳, std::marker::Unpin⮳, std::panic::UnwindSafe⮳, and std::panic::RefUnwindSafe⮳ - see special traits⮳).

dyn Trait
dyn Trait + Send
dyn Trait + Send + Sync
dyn Trait + 'static

See also

Trait Objects (docs)

Attributes

Attributes

Attributes can take arguments with different syntaxes:

#[attribute = "value"]
#[attribute(key = "value")]
#[attribute(value)]
#[attribute(value, value2)]

Inner attributes #![attr] apply to the item that the attribute is declared within.

Lint attributes

During early development, place the following attributes at the top of main.rs or lib.rs

#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]
#![allow(unused_must_use)]
// or simply: #![allow(unused)]
#![allow(dead_code)]
#![allow(missing_docs)]

// This import is not used anywhere
use std::thread;

// This struct is public but is not documented
pub struct S;

#[must_use]
fn required() -> u32 {
    42
}

// Nothing calls this function
fn dead_code() {}

fn main() {
    // This variable is not used
    let x = 1;
    // This mutable variable is not used
    let mut m = 2;
    // The return value of this function is not used
    required();
    println!("Done!");
}

For production-ready code, replace the above by the following, for example.

//! Crate documentation goes here.
#![warn(unused, missing_debug_implementations, missing_docs, rust_2018_idioms)]
// You may also add `missing_copy_implementations` if desirable.
// It detects potentially-forgotten implementations of Copy for public types.

// `deny` creates an error in case of violation
#![deny(unreachable_pub)]
// Prohibit unsafe blocks / functions
// `forbid` is the same as `deny`, but also forbids changing the lint level
// afterwards
#![forbid(unsafe_code)]

// WARNING: fn dead_code() {}

// ERROR: unsafe fn unsafe_func() {}

// ERROR
// fn unsafe_block() {
//     unsafe {
//     }
// }

/// This is the required documentation for S
/// We had to derive Debug to avoid a warning
#[derive(Debug)]
pub(crate) struct S;

/// Here is the required documentation
/// for the main function.
fn main() {
    let s = S;
    println!("{:?}", s);
}

You also apply these attributes to specific functions:

// Disables the `dead_code` lint
#[allow(dead_code)]
fn unused_function() {}

fn main() {
    println!("Nobody is calling `unused_function`.");
}

List of lint checks: rustc -W help. rustc⮳ also recognizes the tool lints for "clippy" and "rustdoc" e.g. #![warn(clippy::pedantic)]

Automatically derive common traits

See Automatic derivation.

Mark as must use

// Must use the results of the fn
// Also applies to traits, structs, enums...
#[must_use]
fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    println!("{}", add(1, 2));
}

Mark as deprecated

#![allow(deprecated)]

// Mark a function as deprecated
// That also works for structs, enums, etc...
#[deprecated(since = "5.2.0", note = "Use bar instead")]
pub fn foo() {
    println!("foo");
}

fn main() {
    // Use of a deprecated item
    foo();
    // Normally we would get a warning.
    // In this case, we used the module-wide #![allow(deprecated)] attribute
    // (first line above) to suppress it.
}

Compile conditionally

Conditional compilation

// This function only gets compiled if the target OS is linux
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
    println!("You are running Linux!");
}

// And this function only gets compiled if the target OS is *not*
// linux
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
    println!("You are *not* running Linux!");
}

fn main() {
    are_you_on_linux();

    println!("Are you sure?");
    if cfg!(target_os = "linux") {
        // alternative: use cfg!
        println!("Yes. It's definitely Linux!");
    } else {
        println!("Yes. It's definitely *not* Linux!");
    }
}

See also

Attributes reference

Rust by example - attributes

Generics

Rust by example - Generics

Generic structs


use std::fmt::Display;

struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

impl Point<f32> {
    // specify constraints on generic types
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

impl<T: Display + PartialOrd> Point<T> {
    // use Trait Bounds to Conditionally Implement Methods
    fn cmp_display(&self) {
        if self.x >= self.y {
            println!("The largest member is x = {}", self.x);
        } else {
            println!("The largest member is y = {}", self.y);
        }
    }
}

fn main() {
    let p = Point { x: 5, y: 10 };

    println!("p.x = {}", p.x());
}

Lifetimes

Rust by example - Lifetimes

Prevent dangling references.

&i32 a reference &'a i32 a reference with an explicit lifetime &'a mut i32 a mutable reference with an explicit lifetime

// 'static indicates that the data pointed to by the reference lives
// for the _remaining_ lifetime of the running program. It can still
// be coerced to a shorter lifetime.
fn my_string() -> &'static str {
    let s: &'static str = "I have a static lifetime.";
    s
}

fn main() {
    println!("{}", my_string());
}

The generic lifetime 'a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

fn main() {
    let (x, y) = ("short", "looooooong");
    println!("{}", longest(x, y));
}

Lifetime Annotations in Struct Definitions and methods


struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl ImportantExcerpt<'_> {
    fn level(&self) -> i32 {
        3
    }
}

fn main() {
    let ie = ImportantExcerpt { part: "a part" };
    println!("{}", ie.level());
}

Modules

Rust by example - Modules

Crates can contain modules.

Declaring modules: In the crate root file (main.rs or lib.rs), you can declare new modules; say, you declare a “garden” module with mod garden; (or pub mod garden; for public); The compiler will look for the module’s code in these places:

  • Inline, within curly brackets that replace the semicolon following mod garden
  • In the file src/garden.rs
  • In the file src/garden/mod.rs (older style)

In any file other than the crate root, you can declare submodules. For example, you might declare mod vegetables; in ``src/garden.rs`. The compiler will look for the submodule’s code within the directory named for the parent module in these places:

  • Inline, directly following mod vegetables, within curly brackets instead of the semicolon
  • In the file src/garden/vegetables.rs
  • In the file src/garden/vegetables/mod.rs (older style)

In Rust, all items (functions, methods, structs, enums, modules, and constants) are private to parent modules by default. Items can access other items in the same module, even when private.

Items in a parent module can’t use the private items inside child modules, but items in child modules can use the items in their ancestor modules.

book-rust-by-example-visibility-rules

A clear explanation of Rust’s module system

use keyword

Create a shortcut to a path with the use⮳ keyword once, and then use the shorter name everywhere else in the scope.

book-rust-by-example-use


// With the following, `File` without prefix is available in the scope
// For code from an external crate, the absolute path begins with the
// crate name - here, the standard `std` library
use std::collections::HashMap;
// Glob - all public objects in `collections` are now in scope
// Use sparingly
use std::collections::*;
// Use `as` to define aliases, for example in case of name conflict
use std::fmt::Result;
use std::fs::File;
use std::io::Result as IoResult;
// The following is equivalent to `use std::io; use std::io::Write;`
use std::io::{self, Write};
// You can combine multiple `use` lines together with { } as well
use std::{cmp::Ordering, fmt};

mod a {
    pub mod b {
        pub fn fn_in_b() {
            println!("in b!");
        }
    }
    pub struct A;
}
// For internal code, a relative path starts from the current module and uses
// `self`, or an identifier in the current module.
use self::a::b;
// b is now in scope

// Try the simpler version:
// use a::b;

fn do_something() {
    b::fn_in_b();
}

mod c {
    // We can construct relative paths that begin in the parent module,
    // rather than the current module or the crate root, by using `super`
    // at the start of the path.
    use super::a;
    // a is now in scope

    pub fn do_something_else() {
        let _a = a::A;
        println!("do_something_else");
    }
}

mod d {
    pub fn let_try_this() {}
}
// Absolute paths start with the literal `crate`.
// You can try:
// use crate::d;

mod e {
    pub mod f {
        pub fn try_that() {
            println!("try_that");
        }
    }
}
// `pub use` re-exports the `f` module from the
// root module, thus external code can use the path
// `<crate_name>::f::try_that()` instead of
// `<crate_name>::e::f::try_that()`.
pub use e::f;

fn main() {
    do_something();
    c::do_something_else();
    // You can of course access the item made public by `pub use` from your
    // module
    f::try_that();
}

Idiomatic - bringing the function’s parent module into scope, not the function itself:


// We bring the hosting module in scope...
use front_of_house::hosting;

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {
            println!("add_to_waitlist");
        }
    }
}

fn eat_at_restaurant() {
    // ...then we access the function within
    hosting::add_to_waitlist();
}

fn main() {
    eat_at_restaurant();
}

On the other hand, when bringing in structs, enums, and other items with use, it’s idiomatic to specify the full path.

// Bring `HashMap` in scope
use std::collections::HashMap;

fn main() {
    // We now refer to `HaspMap` without using its path
    let mut mymap: HashMap<u32, String> = HashMap::new();

    // Let's add something to it then print...
    mymap.entry(42).or_insert("my favorite number".into());
    println!("{:?}", mymap);
}

Match, if let, while let

Rust by example - match


enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(String),
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            // pattern binding to value
            println!("State quarter from {:?}!", state);
            25
        } // if needed, use catchall _ =>
    }
}

fn main() {
    println!("{}", value_in_cents(Coin::Penny));
}

// struct pattern matching
struct Point {
    x: i32,
    y: i32,
    z: i32,
}

fn main() {
    let origin = Point { x: 0, y: 0, z: 0 };

    match origin {
        // Ignoring all fields of a Point except for x by using ..
        Point { x, .. } => println!("x is {}", x),
    }
}

Patterns accept 1 | 2 for or, 1..=5 for inclusive range, if x % 2 == 0 guards, @-binding Message::Hello { id: id_variable @ 3..=7,}.

if let

fn main() {
    let config_max = Some(3u8);
    if let Some(max) = config_max {
        // <-- if let
        println!("The maximum is configured to be {}", max);
    }
}

while let

    let mut stack = Vec::new();

    stack.push(1);
    stack.push(2);
    stack.push(3);

    while let Some(top) = stack.pop() {
        // <-- while let
        println!("{}", top);
    }

See also

Pattern matching

Closures

Closures

Rust by example - Closures

fn find_emails(list: Vec<String>) -> Vec<String> {
    list.into_iter()
        .filter(|s| s.contains('@')) // <-- closure
        .collect()
}

fn main() {
    for s in find_emails(vec![
        String::from("example"),
        String::from("example@example.com"),
    ]) {
        println!("{}", s);
    }
}

Closure with type annotations

use std::thread;
use std::time::Duration;

fn main() {
    // closure can use type annotation. Multiple statements can be
    // enclosed in a block.
    let _expensive_closure = |num: u32| -> u32 {
        println!("Calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        num
    };
}

Closures can capture variables

  • by reference: &T
  • by mutable reference: &mut T
  • by value: T

They preferentially capture variables by reference and only go lower when required.

To force a move:

use std::thread;

fn main() {
    let list = vec![1, 2, 3];
    println!("Before defining closure: {:?}", list);

    // `move` forces the closure to take ownership of the values it uses.
    thread::spawn(move || println!("From thread: {:?}", list))
        .join()
        .unwrap();
}

Closures as input parameters


// A function which takes a closure as an argument and calls it.
// <F> denotes that F is a "Generic type parameter"
fn apply<F>(f: F)
where
    F: FnOnce(),
{
    // The closure takes no input and returns nothing.
    // could also be `Fn` or `FnMut`.
    f();
}

// A function which takes a closure and returns an `i32`.
fn apply_to_3<F>(f: F) -> i32
where
    // The closure takes an `i32` and returns an `i32`.
    F: Fn(i32) -> i32,
{
    f(3)
}

fn main() {
    apply(|| println!("Applied"));
}
  • std::ops::Fn⮳: the closure uses the captured value by reference (&T)
  • std::ops::FnMut⮳: the closure uses the captured value by mutable reference (&mut T)
  • std::ops::FnOnce⮳: the closure uses the captured value by value (T)

Functions may also be used as arguments.

Iterators

fn main() {
    let vec1 = vec![1, 2, 3];
    let vec2 = vec![4, 5, 6];

    // `iter()` for vecs yields `&i32`. Destructure to `i32`. `iter()`
    // only borrows `vec1` and its elements, so they can be used again
    println!("2 in vec1: {}", vec1.iter().any(|&x| x == 2));
    // `into_iter()` for vecs yields `i32`. No destructuring required.
    // `into_iter()` does move `vec2` and its elements, so they cannot be
    // used again
    println!("2 in vec2: {}", vec2.into_iter().any(|x| x == 2));
}

See also

Iterators

Macros

Rust reference - Macros

Rust by example - MacrosRust by example - macros

The Little Book of Rust Macros

fn main() {
    // Macro used as an expression
    // Define a vector
    let _x = vec![1, 2, 3];

    // Macro used as a statement
    // Print the string
    println!("Hello!");

    // Macro definition
    macro_rules! pat {
        ($i:ident) => {
            Some($i)
        };
    }

    // Macro used in a pattern
    // Destructure an Option
    if let pat!(x) = Some(1) {
        assert_eq!(x, 1);
    }

    print_tuple((1, 3));
    use_thread_local();
}

macro_rules! Tuple {
    { $A:ty, $B:ty } => { ($A, $B) };
}

// Macro used in a type
// Define a tuple type
type T2 = Tuple!(i32, i32);

fn print_tuple(tupl: T2) {
    println!("{} {}", tupl.0, tupl.1);
}

fn use_thread_local() {
    use std::cell::RefCell;
    // Macro used as an item
    thread_local!(static FOO: RefCell<u32> = const { RefCell::new(1) });
}

macro_rules! const_maker {
    ($t:ty, $v:tt) => {
        const CONST: $t = $v;
    };
}

#[allow(dead_code)]
trait T {
    // Macro used as an associated item
    const_maker! {i32, 7}
}

// Macro calls within macros.
//
// When used, the outer macro `example` is expanded,
// then the inner macro `println` is expanded.
macro_rules! _example {
    () => {
        println!("Macro call in a macro!")
    };
}

Standard Library

Option

Result

RecipeCrates
Resultstd

Vectors

RecipeCrates
Vecstd

HashMap

RecipeCrates
Hashmapsstd

Strings

Copy-on-write

Smart Pointers

RecipeCrates
Boxstd
Rcstd
RefCellstd

Automatic Trait Derivation

Asref

RecipeCrates
AsRef and &Tstd

Option

Option

std

Rust has no null. Instead, use std::option::Option⮳:

enum Option<T> {
  None,
  Some(T),
}

Every std::option::Option⮳ is either std::option::Option::Some⮳ and contains a value, or std::option::Option::None⮳, and does not.

fn main() {
    let _some_number = Some(5);

    let absent_number: Option<i32> = None;
    println!("{:?}", absent_number);
}

It is often used with match⮳, if let, or while let:

fn bake_cake(sprinkles: Option<&str>) -> String {
    let mut cake = String::from("A delicious cake...");

    // Add required ingredients

    // Handle optional sprinkles
    if let Some(sprinkle_choice) = sprinkles {
        cake.push_str(
            format!(" with a sprinkle of {}", sprinkle_choice).as_str(),
        );
    } else {
        // sprinkles is None
        cake.push_str(" ready for your decorating touch!");
    }
    cake
}

fn main() {
    print!("{}", bake_cake(Some("rainbow nonpareils")));
}

Use adapters when working with references

std

Extract the value contained in Option

std

These methods extract the contained value in an std::option::Option when it is the Some variant. If the std::option::Option⮳ is None:

Use combinators

std


use std::fs;

fn read_file(filename: &str) -> Option<String> {
    fs::read_to_string(filename)
        // Convert `Result` to `Option`
        .ok()
        // `and_then` applies a function to the wrapped value if it's Some.
        .and_then(|contents| Some(contents.trim().to_string()))
}

fn main() -> anyhow::Result<()> {
    if !std::fs::exists("temp")? {
        std::fs::create_dir("temp")?;
    }
    fs::write("temp/poem.txt", b"Lorem ipsum")?;
    let contents = read_file("temp/poem.txt");

    // Using `match` to process the returned Option.
    match contents {
        Some(poem) => println!("{}", poem),
        None => println!("Error reading file"),
    }
    Ok(())
}

Result

RecipeCrates
Resultstd

Result

use std::fs::File;
use std::io;
use std::io::Read;
use std::num::ParseIntError;

// Result  is a type that represents either success (Ok) or failure (Err).
// pub enum Result<T, E> {
//     Ok(T),
//     Err(E),
// }

// Faillible functions like `File::open` return a `Result`...
fn open_file(file_path: &str) {
    let open_result: Result<File, io::Error> = File::open(file_path);
    // You could handle their `Result` there and then...
    match open_result {
        Err(e) => eprintln!("An error occurred while opening the file: {}", e),
        Ok(file) => println!("Opened {:?}", file),
    }
}

// ...but most often you'll want to return early when encountering an error,
// and propagate the error up the call stack.
#[allow(clippy::question_mark)]
fn read_file(file_path: &str) -> Result<String, io::Error> {
    let mut file = match File::open(file_path) {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    // This said, having to use multiple `match` or `if let` is verbose
    // when chaining calls to faillible methods...
    let mut contents = String::new();
    if let Err(e) = file.read_to_string(&mut contents) {
        Err(e)
    } else {
        Ok(contents)
    }
}

// Therefore, use the ? operator as a shortcut to return early
// in case of an error. The following is equivalent to the previous function.
fn read_file2(file_path: &str) -> Result<String, io::Error> {
    let mut file: File = File::open(file_path)?;
    // Note that file is of type `File`, not `io::Result<File> = Result<File,
    // io::Error>`
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}
// You can even chain method calls immediately after the ?, e.g.
// File::open(file_path)?.read_to_string(&mut contents)?;

// You will often need to return one of multiple Result types.
// You could create a custom error `enum` to do so:
fn read_and_parse_file(file_path: &str) -> Result<i32, MyError> {
    let mut file = File::open(file_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?; // `read_to_string` returns `Result<_, io::Error>`
    let number = contents.trim().parse()?; // `parse` returns `Result<_, std::num::ParseIntError>`
    Ok(number)
}

#[allow(dead_code)]
#[derive(Debug)]
enum MyError {
    Io(io::Error),
    Parse(ParseIntError),
}

// That custom error type must implement the `From` trait.
impl From<io::Error> for MyError {
    fn from(err: io::Error) -> MyError {
        MyError::Io(err)
    }
}
impl From<ParseIntError> for MyError {
    fn from(err: ParseIntError) -> MyError {
        MyError::Parse(err)
    }
}

// The `thisError` crate provides a convenient derive macro
// for the standard library’s `std::error::Error` trait.
// Use when writing libraries.
#[allow(dead_code)]
#[derive(thiserror::Error, Debug)]
enum MyError2 {
    #[error("Io error")]
    Io(#[from] io::Error),
    #[error("Parse error")]
    Parse(#[from] ParseIntError),
}

// A simpler method than returning a custom error type
// may be to return a trait object (at the cost of opacity)...
fn read_and_parse_file2(
    file_path: &str,
) -> Result<i32, Box<dyn std::error::Error>> {
    let mut file = File::open(file_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    let number = contents.trim().parse()?;
    Ok(number)
}

// ...but crates like `anyhow` can simplify error management a great deal...
// `anyhow::Result<T>` is equivalent to `std::result::Result<T, anyhow::Error>`.
fn read_and_parse_file3(file_path: &str) -> anyhow::Result<i32> {
    let mut file = File::open(file_path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    let number = contents.trim().parse()?;
    Ok(number)
}

// A function without return value in truth returns `Unit`:
// `fn func() {...}` is a shorthand for `fn func() -> () { ... }`
// To convert it to a faillible function, return `Result<(), SomeErrorType>`.
fn unit_return_value(file_path: &str) -> anyhow::Result<()> {
    let _i: i32 = read_and_parse_file3(file_path)?;
    println!("I don't return anything!");
    // Do not forget to return an Result in the happy path.
    // The double parentheses are required. It is `Ok( () )`.
    Ok(())
}

// `main()` can return a `Result`, more precisely a `Result<T, E> where T:
// Termination, E: Debug)`. `Result<(), Box<dyn std::error::Error>>`,
// `anyhow::Result<()>` are common choices.
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file_path = "example.txt";
    open_file(file_path);
    // Here we ignore the `Result` return values
    // to avoid returning early and exercise all the code.
    // You should rarely need to do this.
    let _ = read_file(file_path);
    let _ = read_file2(file_path);
    let _ = read_and_parse_file(file_path);
    let _ = read_and_parse_file2(file_path);
    let _ = read_and_parse_file3(file_path);
    let _ = unit_return_value(file_path);
    Ok(())
}

See also

Vectors

RecipeCrates
Vecstd

Vec

std

Vectors can only store values that are the same type.

fn main() {
    let mut v: Vec<i32> = Vec::new();
    v.push(5);
    v.push(6);

    let mut v = vec![1, 2, 3]; // or vec!(1, 2, 3)

    let _third: &i32 = &v[2]; // read

    let _third: Option<&i32> = v.get(2);

    for i in &v {
        println!("{i}");
    }

    for i in &mut v {
        *i += 50; // dereference operator
    }
}

Hashmaps

RecipeCrates
Hashmapsstd

std

All of the hashmap keys must have the same type as each other, and all of the values must have the same type.

use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);

    // Update the value
    scores.insert(String::from("Blue"), 25);

    let team_name = String::from("Blue");
    // Get an Option<i32> rather than an Option<&i32>, then unwrap_or to
    // set score to zero if scores doesn't have an entry for the key.
    let _score = scores.get(&team_name).copied().unwrap_or(0);

    // Enumerate
    for (key, value) in &scores {
        println!("{key}: {value}");
    }

    // Adding a Key and Value Only If a Key Isn’t Present
    scores.entry(String::from("Yellow")).or_insert(50);
}

Strings

String

std

fn main() {
    // `String` is Unicode, not ASCII
    let mut s1 = String::from("hello");
    s1.push_str(", world!"); // `String` can be mutated
    s1.clear(); // Empties the String, making it equal to ""

    // Alternative initialization from string literals
    // `to_string` is available on any type that implements
    // the Display trait
    let s2 = "initial contents".to_string();

    // Concatenation: note s1 has been moved here and can no longer
    // be used afterwards
    let s3 = s1 + &s2;
    // ERROR let s = format!("{s1}-{s2}-{s3}");

    // String slice -  contains the first 4 bytes of the string.
    let _s: &str = &s3[0..4];
    // Caution: If we were to try to slice only part of a unicode
    // character’s bytes, Rust would panic at runtime.

    // Iteration
    for c in "Зд".chars() {
        println!("{c}");
    }
    for b in "Зд".bytes() {
        println!("{b}");
    }
}

Placeholders

std

fn main() {
    let x = 5;
    let y = 10;

    println!("x = {x} and y + 2 = {}", y + 2);
}

Use {:?} to use the std::fmt::Debug⮳ output format (annotate type with #[derive(Debug)] ) or {:#?} for pretty print.

Also use dbg!(&rect1); for debug output (returns ownership of the expression’s value).

Concatenate strings

std

Here are several common methods to concatenate Strings:


#[macro_use(concat_string)]
extern crate concat_string;

#[macro_use(concat_strs)]
extern crate concat_strs;

static DATE: &str = "2024-01-15";
static T: &str = "T";
static TIME: &str = "12:00:09Z";

fn main() {
    let _datetime = &[DATE, T, TIME].concat();

    let _datetime = &[DATE, TIME].join(T);

    let _datetime = &[DATE, T, TIME].join("");

    let _datetime = &[DATE, T, TIME].join("");

    let list = [DATE, T, TIME];
    // let _datetime: String = list.iter().map(|x| *x).collect();
    let _datetime: String = list.iter().copied().collect();

    let list = vec![DATE, T, TIME];
    // let _datetime: String = list.iter().map(|x| *x).collect();
    let _datetime: String = list.iter().copied().collect();

    let _datetime = &format!("{}{}{}", DATE, T, TIME);

    let _datetime = &format!("{DATE}{T}{TIME}");

    let mut datetime = String::new();
    datetime.push_str(DATE);
    datetime.push_str(T);
    datetime.push_str(TIME);

    let mut datetime = Vec::<String>::new();
    datetime.push(String::from(DATE));
    datetime.push(String::from(T));
    datetime.push(String::from(TIME));
    let _datetime = datetime.join("");

    let mut datetime = String::with_capacity(20);
    datetime.push_str(DATE);
    datetime.push_str(T); // or 'T'
    datetime.push_str(TIME);

    let _datetime =
        &(String::from(DATE) + &String::from(T) + &String::from(TIME));

    let _datetime = &(String::from(DATE) + T + TIME);

    let _datetime = &(DATE.to_owned() + T + TIME);

    let _datetime = &(DATE.to_string() + T + TIME);

    let _datetime = concat_string!(DATE, T, TIME);

    let datetime = &concat_strs!(DATE, T, TIME);

    println!("{}", datetime);
}

Examples from concatenation_benchmarks-rs

Copy-on-Write

std

The type std::borrow::Cow is a smart pointer providing clone-on-write functionality.

Convert Cow to &str

Use std::borrow::Borrow⮳:

fn main() {
    use std::borrow::Borrow;
    let mut my_string = String::new();
    let example = std::borrow::Cow::from("Example");
    my_string.push_str(example.borrow());
    println!("{}", my_string);
}

Use std::convert::AsRef⮳:

fn main() {
    let mut my_string = String::new();
    let example = std::borrow::Cow::from("Example");
    my_string.push_str(example.as_ref());
    println!("{}", my_string);
}

Use std::ops::Deref⮳ explicitly:

fn main() {
    use std::ops::Deref;
    let mut my_string = String::new();
    let example = std::borrow::Cow::from("example");

    my_string.push_str(example.deref());
    println!("{}", my_string);
}

Use std::ops::Deref⮳ implicitly through a coercion:

fn main() {
    let mut my_string = String::new();
    let example = std::borrow::Cow::from("example");
    my_string.push_str(&example);
    println!("{}", my_string);
}

Convert Cow to String

std

Use std::string::ToString⮳:

fn main() {
    let example = std::borrow::Cow::from("example");
    let s = example.to_string();
    println!("{}", s);
}

Use std::borrow::Cow::into_owned⮳:

fn main() {
    let example = std::borrow::Cow::from("example");
    println!("{}", example.into_owned());
}

Use any method to get a reference and then call std::borrow::ToOwned⮳:

fn main() {
    let example = std::borrow::Cow::from("example");
    println!("{}", example.as_ref().to_owned());
}

These examples were adapted from a StackOverflow discussion

Smart Pointers

RecipeCrates
Boxstd
Rcstd
RefCellstd
  • Rc<T> enables multiple owners of the same data; Box<T> and RefCell<T> have single owners.
  • Box<T> allows immutable or mutable borrows checked at compile time; Rc<T> allows only immutable borrows checked at compile time; RefCell<T> allows immutable or mutable borrows checked at runtime.
  • Because RefCell<T> allows mutable borrows checked at runtime, you can mutate the value inside the RefCell<T> even when the RefCell<T> is immutable.

Box

book-rust-box Rust by example - box std

All values in Rust are stack allocated by default. Box<T> allow you to store data on the heap rather than the stack. What remains on the stack is the pointer to the heap data.

Boxes provide ownership for this allocation, and drop their contents when they go out of scope. Boxes also ensure that they never allocate more than isize::MAX bytes.

The Box<T> type is a smart pointer, because it implements the std::ops::Deref⮳ trait, which allows Box<T> values to be treated like a reference. You can use the dereference operator * or deref coercion with the . operator to retrieve its inner value.

let boxed: Box<u8> = Box::new(1);
let _val: u8 = *boxed;
let boxed = Box::new("example");
// Deref coercion: equivalent to (*boxed.deref()).len()
let _val = boxed.len();

Use Box<T> when

  • you have a dynamically sized type, whose size can’t be known at compile time,
  • you want to own a value and you care only that it’s a type that implements a particular trait rather than being of a specific type,
  • you don't want to rely on stack space.

// Define a Node struct to represent a single element in a linked list.
struct Node {
    value: i32,
    // Node is a recursive data type.
    next: Option<Box<Node>>,
}

impl Node {
    fn new(value: i32) -> Self {
        Node { value, next: None }
    }

    // Recursively traverses the list until it finds the last node
    // (where next is None) and sets its next field to a new Node.
    fn append(&mut self, value: i32) {
        match self.next {
            Some(ref mut next_node) => next_node.append(value),
            None => self.next = Some(Box::new(Node::new(value))),
        }
    }

    fn print(&self) {
        print!("{}", self.value);
        if let Some(ref next_node) = self.next {
            print!(" -> ");
            next_node.print();
        } else {
            println!();
        }
    }
}

fn main() {
    // The linked list has an unknown number of nodes,
    // thus its size is not fixed.
    // It could not be stored directly on the stack.
    // By using `Box`, which pointer to the heap has a defined size,
    // we can create the `head` local variable on the stack.
    let mut head = Node::new(1);
    head.append(2);
    head.append(3);
    head.append(4);

    head.print(); // Output: 1 -> 2 -> 3 -> 4
}

Rc

std

The Rc<T> type keeps track of the number of references to data on the heap so that data can have multiple owners.

fn main() {
    todo!();
}

RefCell

std

The RefCell<T> type with its interior mutability gives us a type that we can use when we need an immutable type but need to change an inner value of that type; it also enforces the borrowing rules at runtime instead of at compile time.

use std::cell::RefCell;

// The `RefCell` type in Rust is used for _interior mutability_,
// a pattern that allows you to mutate data even when there are immutable
// references to it. `RefCell` dynamically borrow checks at runtime,
// unlike Rust's standard borrowing rules (checks at compile time).
// Attempts to violate borrowing rules (like having multiple mutable borrows)
// will cause a panic at runtime. `RefCell` is single-threaded.
// The corresponding `Sync` version of `RefCell<T>` is `RwLock<T>`.
fn main() {
    // Create a RefCell containing a vector of integers
    let data = RefCell::new(vec![1, 2, 3, 4, 5]);

    // Borrow the data immutably
    {
        let borrowed_data = data.borrow();
        println!("Borrowed (immutable): {:?}", borrowed_data);
    } // The immutable borrow ends here

    // Borrow the data mutably and modify it
    {
        let mut borrowed_data = data.borrow_mut();
        borrowed_data.push(6);
        println!("Borrowed (mutable): {:?}", borrowed_data);
    } // The mutable borrow ends here

    // Borrow the data immutably again to check the modification
    {
        let borrowed_data = data.borrow();
        println!("Borrowed (immutable again): {:?}", borrowed_data);
    }
}

AsRef and &T

RecipeCrates
AsRef and &Tstd

When and why to use AsRefinstead of &T

fn main() {
    todo!();
}

Automatic trait derivation

std

The derive⮳ attribute generates code that will implement a trait with its own default implementation on the type you’ve annotated with the derive syntax.

Derivable traits

// On structs
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Default)]
struct S(i32);

fn main() {
    println!("{:?}", S(0));
    println!("{}", S(1) == S(1));
}

You can use the cargo_expand utility to see the exact code that is generated for your specific type.

See also:

Derive More

derive_more

Derive More (crates)⮳ derive lots of additional, commonly used traits and static methods for both structs and enums.

use derive_more::Add;
use derive_more::Display;
use derive_more::From;
use derive_more::Into;

#[derive(PartialEq, From, Add)]
struct MyInt(i32);

#[derive(PartialEq, From, Into)]
struct Point2D {
    x: i32,
    y: i32,
}

#[derive(PartialEq, From, Add, Display)]
enum MyEnum {
    #[display("int: {}", _0)]
    Int(i32),
    Uint(u32),
    #[display("nothing")]
    Nothing,
}

fn main() {
    assert!(MyInt(11) == MyInt(5) + 6.into());
    assert!((5, 6) == Point2D { x: 5, y: 6 }.into());
    assert!(MyEnum::Int(15) == (MyEnum::Int(8) + 7.into()).unwrap());
    assert!(MyEnum::Int(15).to_string() == "int: 15");
    assert!(MyEnum::Uint(42).to_string() == "42");
    assert!(MyEnum::Nothing.to_string() == "nothing");
}

Key crates

Consult the following sites for crate recommendations:

or older resources, such as:

Best of Rust crates

Crates

Crates mentioned in this book, by alphabetic order.

A

actix-web anyhow approx arrow async-channel async-std async-stream async-trait axum

B

bacon base64 bat bevy bitflags bonsai-bt broot byteorder bytes

C

candle_core cargo cargo-auditable cargo-cache cargo-crates cargo-edit cargo-expand cargo-generate cargo-hack cargo-hakari cargo-husky cargo-limit cargo-machete cargo-make cargo-nextest cargo-tarpaulin cargo-udeps cargo-wizard cargo-xtask cc chrono clap concat-string concat_strs config confy cornucopia cosmic-text crates_io_api cross crossbeam crossbeam-channel crossbeam-queue crossbeam-utils crux_core csv

D

dashmap data-encoding datafusion derive_more diesel digest dotenv dotenvy druid duct dyn-clone

E

egui elasticsearch embassy env_logger envy exa eyre

F

flagset flate2 floem form-urlencoded futures futures-executor

G

glidesort glob gping

H

http http-body-util hyper

I

iced image indicatif infisearch itertools

J

just

K

kanal kani

L

lazy_static lens leptos linfa loco_rs log log4rs lru lsd

M

mdbook mdbook-cmdrun mdbook-hide mdbook-journal mdbook-keeper mdbook-linkcheck mdbook-pagetoc mdbook-private mdbook-tera mdbook-theme mdbook-toc meilisearch memmap2 miette mime minisearch mio miri mongodb monostate multimap

N

nalgebra native-windows-gui ndarray nom notify num num_cpus

O

once_cell open opencv openrr

P

parking_lot paste percent-encoding pest plotly polars postage postgres proc-macro2 pyo3 pyoxidizer

Q

quote

R

r3bl_tuify rand rand_distr ratatui rayon redis regex reqwest rhai riker ring roogle ruff rui rusqlite rustdesk rustquant

S

salsa same-file sea-orm seaography select semver serde serde_json sled slint slotmap smol sqlx stakker starship std stork-search swc_ecma_parser syn syslog

T

tantivy tar tauri tempfile termbook thiserror threadpool tinysearch tokio tokio-graceful-shutdown toml tonic tower tower-http tracing tracing-subscriber trillium typesense

U

unicode-segmentation url

W

walkdir wasmtime watchmaker watt wgpu windows

X

xilem xshell xsv

Y

yew

Z

zed zenoh

Crates by category

CategoryDescriptionCrates
cat-api-bindingsIdiomatic wrappers of specific APIs for convenient access from Rust. Includes HTTP API wrappers as well. Non-idiomatic or unsafe bindings can be found in External FFI bindings.elasticsearch flate2 pyo3
cat-accessibilityAssistive technology that helps overcome disabilities and impairments to make software usable by as many people as possible.xilem
cat-algorithmsRust implementations of core algorithms such as hashing, sorting, searching, and more.crossbeam-channel crossbeam-utils dashmap glidesort itertools linfa num rand rand_distr rustquant sled
cat-asynchronousCrates to help you deal with events independently of the main program flow, using techniques like futures, promises, waiting, or eventing.actix-web async-channel async-std async-trait axum futures kanal mio mongodb postage smol stakker tokio tokio-graceful-shutdown tonic tower tower-http tracing tracing-subscriber
cat-development-tools::build-utilsUtilities for build scripts and other build time steps.cargo-make cc xshell
cat-cachingCrates to store the results of previous computations in order to reuse the results.lru sled slotmap
cat-development-tools::cargo-pluginsSubcommands that extend the capabilities of Cargo.cargo-auditable cargo-cache cargo-crates cargo-edit cargo-expand cargo-hack cargo-hakari cargo-make cargo-nextest cargo-udeps cargo-wizard
cat-command-line-utilitiesApplications to run at the command line.bacon bat broot cargo-cache cargo-crates cargo-hack cargo-make exa infisearch just lens lsd mdbook-journal mdbook-tera r3bl_tuify starship
cat-command-line-interfaceCrates to help create command line interfaces, such as argument parsers, line-editing, or output coloring and formatting.clap indicatif r3bl_tuify ratatui
cat-compressionAlgorithms for making data smaller.flate2
cat-concurrencyCrates for implementing concurrent and parallel computation.async-channel async-std crossbeam crossbeam-channel crossbeam-queue crossbeam-utils dashmap kanal parking_lot rayon sled smol stakker threadpool
cat-configCrates to facilitate configuration management for applications.config envy toml
cat-cryptographyAlgorithms intended for securing data.digest ring
cat-data-structuresRust implementations of particular ways of organizing data suited for specific purposes.bytes crossbeam crossbeam-channel crossbeam-queue crossbeam-utils dashmap kanal ndarray num semver sled slotmap stakker tantivy
cat-database-implementationsDatabases allow clients to store and query large amounts of data in an efficient manner. This category is for database management systems implemented in Rust.sled tantivy
cat-databaseCrates to interface with database management systems.cornucopia diesel elasticsearch mongodb postgres rusqlite sea-orm seaography
cat-date-and-timeCrates to manage the inherent complexity of dealing with the fourth dimension.chrono
cat-development-tools::debuggingCrates to help you figure out what is going on with your code such as logging, tracing, or assertions.cargo-expand env_logger log tracing tracing-subscriber
cat-development-toolsCrates that provide developer-facing features such as testing, debugging, linting, performance profiling, autocompletion, formatting, and more.bacon cargo-cache cargo-edit cargo-husky cargo-make cargo-tarpaulin cargo-udeps concat_strs derive_more just kani paste
cat-embeddedCrates that are primarily useful on embedded devices or without an operating system.rhai
cat-encodingEncoding and/or decoding data from one data format to another.base64 byteorder cargo-auditable csv data-encoding image monostate serde serde_json toml url
cat-multimedia::encodingCrates that encode or decode binary data in multimedia formats.image
cat-development-tools::ffiCrates to help you better interface with other languages. This includes binding generators and helpful language constructs.pyo3
cat-filesystemCrates for dealing with files and filesystems.glob notify tempfile walkdir xshell
cat-financeCrates for dealing with money. Accounting, trading, investments, taxes, banking and payment processing using government-backed currencies.rustquant
cat-guiCrates to help you create a graphical user interface.bevy druid egui iced rui slint tauri xilem yew
cat-game-developmentFor crates that focus on some individual part of accelerating the development of games.egui
cat-game-enginesFor crates that try to provide a "one-stop-shop" for all of your game development needs.bevy
cat-graphicsCrates for graphics libraries and applications, including raster and vector graphics primitives such as geometry, curves, and color.bevy xilem
cat-web-programming::http-clientCrates to make HTTP network requests.crates_io_api hyper reqwest
cat-web-programming::http-serverCrates to serve data over HTTP.actix-web axum hyper trillium
cat-hardware-supportCrates to interface with specific CPU or other hardware features.num_cpus
cat-multimedia::imagesCrates that process or build images.image
cat-internationalizationCrates to help develop software capable of adapting to various languages and regions.xilem
cat-mathematicsCrates with a mathematical aspect.linfa nalgebra rustquant
cat-memory-managementCrates to help with allocation, memory mapping, garbage collection, reference counting, or interfaces to foreign memory managers.crossbeam lazy_static once_cell slotmap
cat-network-programmingCrates dealing with higher-level network protocols such as FTP, HTTP, or SSH, or lower-level network protocols such as TCP or UDP.actix-web async-std axum bytes hyper smol tokio tonic tower tower-http zenoh
cat-no-std::no-allocCrates that are able to function without the Rust alloc crate.itertools monostate paste serde
cat-no-stdCrates that are able to function without the Rust standard library.anyhow async-trait bitflags byteorder crossbeam crossbeam-queue crossbeam-utils data-encoding derive_more digest dyn-clone itertools lazy_static lru monostate nalgebra num paste rand rand_distr rhai ring semver serde serde_json slint tracing
cat-osBindings to operating system-specific APIs.threadpool
cat-parser-implementationsParsers implemented for particular formats or languages.csv rhai serde_json syn toml url
cat-parsingCrates to help create parsers of binary and text formats. Format-specific parsers belong in other, more specific categories.byteorder nom pest toml
cat-development-tools::procedural-macro-helpersCrates to help you write procedural macros in Rust.derive_more proc-macro2 quote syn watt
cat-development-tools::profilingCrates to help you figure out the performance of your code.tracing tracing-subscriber
cat-renderingReal-time or offline rendering of 2D or 3D graphics, usually with the help of a graphics card.bevy
cat-rendering::engineHigh-level solutions for rendering on the screen.slint
cat-science::roboticsCrates related to robotics.openrr
cat-rust-patternsShared solutions for particular situations specific to programming in Rust.anyhow concat-string concat_strs derive_more dyn-clone eyre itertools lazy_static miette monostate once_cell thiserror
cat-scienceCrates related to solving problems involving physics, chemistry, biology, machine learning, geoscience, and other scientific fields.linfa nalgebra ndarray num rustquant
cat-simulationCrates used to model or construct models for some activity, e.g. to.simulate a networking protocol.rustquant
cat-template-engineCrates designed to combine templates with data to produce result documents, usually with an emphasis on processing text.mdbook-journal mdbook-tera
cat-development-tools::testingCrates to help you verify the correctness of your code.cargo-hack cargo-make cargo-nextest
cat-text-processingCrates to deal with the complexities of human language when expressed in textual form.regex
cat-web-programmingCrates to create applications for the web.crates_io_api http http-body-util mongodb tauri tonic tower-http trillium url yew
cat-wasmCrates for use when targeting WebAssembly, or for manipulating WebAssembly.nalgebra reqwest rhai stork-search watt yew
cat-web-programming::websocketCrates to communicate over the WebSocket protocol.actix-web
cat-os::windows-apisBindings to Windows-specific APIs.windows

All Valid Categories

Algorithms

cat-algorithms

Rust implementations of core algorithms, such as hashing, sorting, searching, and more.

Random Numbers

Sorting

Generate Random Values

Generate random numbers

rand cat-algorithms cat-no-std

Generates random numbers with help of the random-number generator rand::Rng. Each thread has an initialized generator. Integers are uniformly distributed over the range of the type, and floating point numbers are uniformly distributed from 0 up to but not including 1.

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();

    // Note: we use a raw identifier to stay compatible with Rust edition 2024.
    // `gen` is a keyword in the 2024 edition.
    let n1: u8 = rng.r#gen();
    let n2: u16 = rng.r#gen();
    println!("Random u8: {}", n1);
    println!("Random u16: {}", n2);
    println!("Random u32: {}", rng.r#gen::<u32>());
    println!("Random i32: {}", rng.r#gen::<i32>());
    println!("Random float: {}", rng.r#gen::<f64>());
}

Generate random numbers within a range

rand cat-algorithms cat-no-std

Generates a random value within half-open [0, 10) range (not including 10) with rand::Rng::gen_range⮳ range.

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    println!("Integer: {}", rng.gen_range(0..10));
    println!("Float: {}", rng.gen_range(0.0..10.0));
}

rand::distributions::uniform::Uniform can obtain values with uniform distribution. This has the same effect, but may be faster when repeatedly generating numbers in the same range.

use rand::distributions::Distribution;
use rand::distributions::Uniform;

fn main() {
    let mut rng = rand::thread_rng();
    let die = Uniform::from(1..7);

    loop {
        let throw = die.sample(&mut rng);
        println!("Roll the die: {}", throw);
        if throw == 6 {
            break;
        }
    }
}

Generate random numbers within a given distribution

rand rand_distr cat-algorithms cat-no-std

By default, random numbers in the rand⮳ crate have uniform distribution⮳. The rand_distr⮳ crate provides other kinds of distributions. To use them, you instantiate a distribution, then sample from that distribution using rand::distributions::Distribution::sample⮳ with help of a random-number generator rand::Rng⮳. The distributions available are documented here⮳. An example using the rand_distr::Normal⮳ distribution is shown below.

use rand::thread_rng;
use rand_distr::Distribution;
use rand_distr::Normal;
use rand_distr::NormalError;

fn main() -> Result<(), NormalError> {
    let mut rng = thread_rng();
    let normal = Normal::new(2.0, 3.0)?;
    let v = normal.sample(&mut rng);
    println!("{} is from a N(2, 9) distribution", v);
    Ok(())
}

Generate random values of a custom type

rand cat-algorithms cat-no-std

Randomly generates a tuple (i32, bool, f64) and variable of user defined type Point. Implements the rand::distributions::Distribution⮳ trait on type Point for rand::distributions::Standard⮳ trait in order to allow random generation.

use rand::Rng;
use rand::distributions::Distribution;
use rand::distributions::Standard;

#[derive(Debug)]
#[allow(dead_code)]
struct Point {
    x: i32,
    y: i32,
}

impl Distribution<Point> for Standard {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
        let (rand_x, rand_y) = rng.r#gen();
        Point {
            x: rand_x,
            y: rand_y,
        }
    }
}

fn main() {
    let mut rng = rand::thread_rng();
    let rand_tuple = rng.r#gen::<(i32, bool, f64)>();
    let rand_point: Point = rng.r#gen();
    println!("Random tuple: {:?}", rand_tuple);
    println!("Random Point: {:?}", rand_point);
}

Create random passwords from a set of alphanumeric characters

rand cat-algorithms

Randomly generates a string of given length ASCII characters in the range A-Z, a-z, 0-9, with rand::distributions::Alphanumeric⮳ sample.

use rand::Rng;
use rand::distributions::Alphanumeric;
use rand::thread_rng;

fn main() {
    let rand_string: String = thread_rng()
        .sample_iter(&Alphanumeric)
        .take(30)
        .map(char::from)
        .collect();
    println!("{}", rand_string);
}

Create random passwords from a set of user-defined characters

rand cat-os

Randomly generates a string of given length ASCII characters with custom user-defined bytestring, with rand::Rng::gen_range.

fn main() {
    use rand::Rng;
    const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                            abcdefghijklmnopqrstuvwxyz\
                            0123456789)(*&^%$#@!~";
    const PASSWORD_LEN: usize = 30;
    let mut rng = rand::thread_rng();

    let password: String = (0..PASSWORD_LEN)
        .map(|_| {
            let idx = rng.gen_range(0..CHARSET.len());
            CHARSET[idx] as char
        })
        .collect();

    println!("{:?}", password);
}

Sorting Vectors

Sort a vector of integers

std cat-science

This example will sort a Vector of integers via std::vec::Vec::sort⮳. Alternative would be to use std::vec::Vec::sort_unstable⮳ which can be faster, but does not preserve the order of equal elements.

fn main() {
    let mut vec = vec![1, 5, 10, 2, 15];

    vec.sort();

    assert_eq!(vec, vec![1, 2, 5, 10, 15]);
    println!("{:?}", vec);
}

Sort a vector of floats

std cat-science

A vector of f32 or f64 can be sorted with sort_by and std::cmp::PartialOrd::partial_cmp⮳.

fn main() {
    let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0];

    vec.sort_by(|a, b| a.partial_cmp(b).unwrap());

    assert_eq!(vec, vec![1.1, 1.123, 1.15, 2.0, 5.5]);

    println!("{:?}", vec);
}

Sort a vector of structs

std cat-science

Sorts a vector of Person structs with properties name and age by its natural order (by name and age). In order to make Person sortable you need four traits std::cmp::Eq⮳, std::cmp::PartialEq⮳, std::cmp::Ord⮳ and std::cmp::PartialOrd⮳. These traits can be simply derived. You can also provide a custom comparator function using a std::vec::Vec::sort_by⮳ method and sort only by age.

#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
struct Person {
    name: String,
    age: u32,
}

impl Person {
    pub fn new(name: String, age: u32) -> Self {
        Person { name, age }
    }
}

fn main() {
    let mut people = vec![
        Person::new("Zoe".to_string(), 25),
        Person::new("Al".to_string(), 60),
        Person::new("John".to_string(), 1),
    ];

    // Sort people by derived natural order (Name and age)
    people.sort();

    assert_eq!(people, vec![
        Person::new("Al".to_string(), 60),
        Person::new("John".to_string(), 1),
        Person::new("Zoe".to_string(), 25),
    ]);
    println!("{:?}", people);

    // Sort people by age
    people.sort_by(|a, b| b.age.cmp(&a.age));

    assert_eq!(people, vec![
        Person::new("Al".to_string(), 60),
        Person::new("Zoe".to_string(), 25),
        Person::new("John".to_string(), 1),
    ]);

    println!("{:?}", people);
}

See also

glidesort⮳ is a Rust implementation of Glidesort, a stable adaptive quicksort/mergesort hybrid sorting algorithm.

Asynchronous programming

cat-asynchronous

Crates to help you deal with events independently of the main program flow, using techniques like futures, promises, waiting, or eventing.

RecipeCratesCategories
Basicstokiocat-asynchronous
Jointokiocat-asynchronous
Spawningtokiocat-asynchronous
IOtokiocat-asynchronous
Graceful shutdowntokio_graceful_shutdowncat-asynchronous
RecipeCratesCategories
Async traitsasync-traitcat-asynchronous
RecipeCratesCategories
Streamsfuturescat-asynchronous

Async

Asynchronous programming, or async for short, is a concurrent programming model supported by an increasing number of programming languages. It lets you run a large number of concurrent tasks, while preserving much of the look and feel of ordinary synchronous programming, through the async/await syntax. It helps you deal with events independently of the main program flow, using techniques like futures, promises, waiting, or eventing.

  • Ability to make progress on multiple tasks, even if they don't execute at the exact same time.
  • Mechanism: cooperative multitasking - tasks yield control, allowing other tasks to run.
  • Involves context switching on a single thread or, most often, among a few threads (the pool of which is opaquely managed by the async runtime).
  • Achieves non-blocking I/O operations to improve responsiveness and efficiency.
  • Lower overhead compared to multithreading.
  • Multi-threaded async programming also requires careful synchronization to prevent data races.

Key constructs in Rust:

Basic example

std tokio cat-asynchronous

use std::future::Future;

struct SomeStruct;

// Most often, we will use async functions.
// Rust transforms the `async fn` at compile time into a state machine
// that _implicitly_ returns a `Future`. A future represents an
// asynchronous computation that might not have finished yet.
async fn first_task() -> SomeStruct {
    // ...
    println!("First task");
    SomeStruct
}

async fn second_task_1(_s: &SomeStruct) {
    // ...
    println!("Second task, part 1");
}

// `async fn` is really syntaxic sugar for a function...
#[allow(clippy::manual_async_fn)]
fn second_task_2() -> impl Future<Output = ()> {
    // ...that contains an `async` block.
    async {
        println!("Second task, part 2");
    } // returns `Future<Output = ()>`
}

async fn do_something() {
    // Use `.await` to start executing the future.
    let s = first_task().await;
    // `await` yields control back to the executor, which may decide to do
    // other work if the task is not ready, then come back here.

    // `join!` is like `.await` but can wait for multiple futures
    // concurrently, returning when all branches complete.
    let f1 = second_task_1(&s);
    let f2 = second_task_2();
    futures::join!(f1, f2); // or tokio::join!
}

// We replace `fn main()` by `async fn main()` and declare which
// executor runtime we'll use - in this case, Tokio. The runtime crate
// must be added to `Cargo.toml`: `tokio = { version = "1", features =
// that transforms it into a synchronous fn main() that initializes a
// runtime instance and executes the async main function.
#[tokio::main]
async fn main() {
    do_something().await;
    // note: `await` must be called or nothing is executing.
    // Futures are lazy
}

As any form of cooperative multitasking, a future that spends a long time without reaching an await⮳ "blocks the thread", which may prevent other tasks from running.

Differences with other languages

std cat-asynchronous

Rust's implementation of async⮳ differs from most languages in a few ways:

  • Rust's async⮳ operations are lazy. Futures are inert in Rust and only make progress only when polled. The executor calls the std::task::Poll⮳ method repeatedly to execute futures.
async fn say_world() {
    println!("world");
}

#[tokio::main]
async fn main() {
    // Calling `say_world()` does not execute the body of `say_world()`.
    let op = say_world();

    // This println! comes first
    println!("hello");

    // Calling `.await` on `op` starts executing `say_world`.
    op.await;
}
// Prints:
// hello
// world
// Example from https://tokio.rs/tokio/tutorial/hello-tokio
  • Dropping a future stops it from making further progress.
  • Async is zero-cost in Rust. You can use async⮳ without heap allocations and dynamic dispatch. This also lets you use async in constrained environments, such as embedded systems.
  • No built-in runtime is provided by Rust itself. Instead, runtimes are provided by community-maintained crates.
  • Both single- and multi-threaded runtimes are available.

Which crate provides what?

std cat-asynchronous

  • The async⮳ / await⮳ syntactic sugar is supported directly by the Rust compiler.
  • The most fundamental traits, types, and functions, such as the std::future::Future⮳ trait, are provided by the standard library.
  • Many utility types, macros and functions are provided by the futures⮳ crate. They can be used in any async Rust application.
  • Execution of async code, IO and task spawning are provided by "async runtimes", such as tokio⮳ and async_std⮳. Most async applications, and some async crates, depend on a specific runtime.

Async runtimes

async-std smol embassy mio cat-asynchronous

In most cases, prefer the tokio runtime - see The State of Async Rust: Runtimes⮳.

Alternatives to the Tokio async ecosystem include:

  • async-std async_std-crates.io⮳: async version of the Rust standard library. No longer maintained?
  • smol Smol
  • embassy Embassyembassy-github for embedded systems.
  • mio Mio⮳ is a fast, low-level I/O library for Rust focusing on non-blocking APIs and event notification for building high performance I/O apps with as little overhead as possible over the OS abstractions. It is part of the Tokio ecosystem.

See also

Are we async yet?

Asynchronous Programming in Rust (book)

Async traits

RecipeCratesCategories
Async traitsasync-traitcat-asynchronous

std cat-asynchronous

As of Rust 1.75, it is possible to have async⮳ functions in traits:

struct MyHealthChecker;

trait HealthCheck {
    async fn check(&mut self) -> bool; // <- async fn defined in a Trait
}

impl HealthCheck for MyHealthChecker {
    async fn check(&mut self) -> bool {
        // async fn implementation in the associated impl block
        do_async_op().await
    }
}

async fn do_health_check(mut hc: impl HealthCheck) {
    if !hc.check().await {
        // use as normal
        log_health_check_failure().await;
    } else {
        println!("Health check was normal");
    }
}

async fn do_async_op() -> bool {
    true
}

async fn log_health_check_failure() {
    println!("Health check failure");
}

#[tokio::main]
async fn main() {
    let hc = MyHealthChecker;
    do_health_check(hc).await;
}

Stabilizing async fn in traits in 2023

This is in turn enabled by return-position impl Trait in traits, since async fn is sugar for functions that return -> impl Future.

#[allow(dead_code)]
trait Container {
    // Return `Impl` in a trait
    fn items(&self) -> impl Iterator<Item = u8>;
}

struct MyContainer {
    items: Vec<u8>,
}

impl Container for MyContainer {
    fn items(&self) -> impl Iterator<Item = u8> {
        self.items.iter().cloned()
    }
}

fn main() {
    let c = MyContainer {
        items: vec![1, 2, 3],
    };
    for i in c.items {
        println!("{}", i);
    }
}

Note that there are still caveats for public traits - see Announcing async fn and return-position impl Trait in traits⮳.

In addition, traits that use -> impl Trait and async fn are not object-safe, which means they lack support for dynamic dispatch. In the meanwhile, use the async-trait crate.

async-trait async_trait-github cat-asynchronous

use async_trait::async_trait;

#[async_trait]
trait Advertisement {
    async fn run(&self);
}

struct Modal;

#[async_trait]
impl Advertisement for Modal {
    async fn run(&self) {
        self.render_fullscreen().await;
        for _ in 0..4u16 {
            remind_user_to_join_mailing_list().await;
        }
        self.hide_for_now().await;
    }
}

impl Modal {
    async fn render_fullscreen(&self) {
        println!("Render fullscreen");
    }

    async fn hide_for_now(&self) {
        println!("Hide for now");
    }
}

async fn remind_user_to_join_mailing_list() {
    println!("Please join our mailing list");
}

#[tokio::main]
async fn main() {
    Modal.run().await;
}

Tokio

RecipeCratesCategories
Basicstokiocat-asynchronous
Jointokiocat-asynchronous
Spawningtokiocat-asynchronous
IOtokiocat-asynchronous
Graceful shutdowntokio_graceful_shutdowncat-asynchronous

Basics

tokio-website tokio tokio-crates.io tokio-github tokio-lib.rs cat-asynchronous cat-network-programming

Tokio is an asynchronous runtime for the Rust programming language. It provides the building blocks needed for writing networking applications. Tokio provides a few major components:

  • Multiple variations of the runtime for executing asynchronous code. Everything from a multi-threaded, work-stealing runtime to a light-weight, single-threaded runtime.

  • An asynchronous version of the standard library.

  • A large ecosystem of libraries.

  • creating and running a runtime, spawning tasks, working with I/O and timers, and handling errors.

Join

By running all async expressions on the current task, the expressions are able to run concurrently but not in parallel. This means all expressions are run on the same thread and if one branch blocks the thread, all other expressions will be unable to continue. If parallelism is required, spawn each async expression using tokio::spawn and pass the join handle to join!.

Spawning

IO

  • read and write data asynchronously with Tokio, using streams, codecs, and futures. It also shows how to handle errors and timeouts.

Current thread runtime

equivalent to

fn main() {
    tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async {
            println!("Hello world");
        })
}

LocalSet

In some cases, it is necessary to run one or more futures that do not implement Send and thus are unsafe to send between threads. In these cases, a local task set may be used to schedule one or more !Send futures to run together on the same thread.

use std::rc::Rc;

use tokio::task;
use tokio::time;

#[tokio::main]
async fn main() {
    // Data that is not thread-safe:
    let nonsend_data = Rc::new("world");

    // A set of tasks which are executed on the same thread:
    let local = task::LocalSet::new();

    let nonsend_data2 = nonsend_data.clone();
    local.spawn_local(async move {
        // ...
        println!("hello {}", nonsend_data2)
    });

    local.spawn_local(async move {
        time::sleep(time::Duration::from_millis(100)).await;
        println!("goodbye {}", nonsend_data)
    });

    // ...

    local.await;
}

Graceful shutdown

tokio-graceful-shutdown tokio-graceful-shutdown-crates.io tokio-graceful-shutdown-github tokio-graceful-shutdown-lib.rs cat-asynchronous

Example from c-tokio_graceful_shutdownc-tokio_graceful_shutdown⮳:

use tokio::time::Duration;
use tokio::time::sleep;
use tokio_graceful_shutdown::SubsystemBuilder;
use tokio_graceful_shutdown::SubsystemHandle;
use tokio_graceful_shutdown::Toplevel;

async fn countdown() {
    for i in (1..=3).rev() {
        tracing::info!("Shutting down in: {}", i);
        sleep(Duration::from_millis(1000)).await;
    }
}

async fn countdown_subsystem(
    subsys: SubsystemHandle,
) -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
    tokio::select! {
        _ = subsys.on_shutdown_requested() => {
            tracing::info!("Countdown cancelled.");
        },
        _ = countdown() => {
            subsys.request_shutdown();
        }
    };
    Ok(())
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Init logging
    tracing_subscriber::fmt().init();

    // Setup and execute subsystem tree
    Toplevel::new(|s| async move {
        s.start(SubsystemBuilder::new("Countdown", countdown_subsystem));
    })
    // Signals the Toplevel object to listen for SIGINT/SIGTERM/Ctrl+C
    .catch_signals()
    // Collects all the return values of the subsystems, determines the global error state
    .handle_shutdown_requests(Duration::from_millis(1000))
    .await
    .map_err(|e| e.into())
}

Channels for use in async code

The most common form of synchronization in an async program is message passing. Two tasks operate independently and send messages to each other to synchronize. Doing so has the advantage of avoiding shared state. Message passing is implemented using async channels.

Tokio's sync⮳ module provides channels that work well with async code.

OneShot

tokio cat-asynchronous

tokio::sync::oneshot⮳ sends a single value from a single producer to a single consumer. This channel is usually used to send the result of a computation to a waiter.

use tokio::sync::oneshot;

async fn some_computation(input: u32) -> String {
    format!("the result of computation is {}", input)
}

async fn one_shot() {
    let (tx, rx) = oneshot::channel();

    tokio::spawn(async move {
        let res = some_computation(0).await;
        tx.send(res).unwrap();
        // Alternatively, return the value via the joinhandle returned
        // by `spawn`
    });

    // Do other work while the computation is happening in the background

    // Wait for the computation result
    let res = rx.await.unwrap();
    println!("{}", res);
}

#[tokio::main]
async fn main() {
    one_shot().await;
}

Another example:

use std::time::Duration;

use tokio::sync::oneshot;

async fn download_file() -> Result<String, std::io::Error> {
    // Simulate downloading a file
    let filename = "data.txt";
    tokio::time::sleep(Duration::from_secs(2)).await;
    println!("Downloaded file: {}", filename);
    Ok(filename.to_owned())
}

async fn process_file(filename: String) {
    // Simulate processing the downloaded file
    println!("Processing file: {}", filename);
    tokio::time::sleep(Duration::from_secs(1)).await;
    println!("Finished processing file.");
}

async fn async_main() -> Result<(), Box<dyn std::error::Error>> {
    let (sender, receiver) = oneshot::channel();

    // Spawn the download task
    tokio::spawn(async move {
        let filename = download_file().await?;
        sender.send(filename).expect("Failed to send filename");
        Ok::<(), std::io::Error>(())
    });

    // Wait for the downloaded filename from the receiver
    let filename = receiver.await?;

    // Spawn the processing task with the filename
    tokio::spawn(async move {
        process_file(filename).await;
    });

    Ok(())
}

fn main() {
    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async { async_main().await }).unwrap();
}

Send messages from multiple producers to a single consumer

tokio cat-asynchronous

use tokio::sync::mpsc;

async fn some_computation(input: u32) -> String {
    format!("the result of computation is {}", input)
}

pub async fn multi_producer_single_receiver() {
    let (tx, mut rx) = mpsc::channel(100);

    tokio::spawn(async move {
        for i in 1..=10 {
            let res = some_computation(i).await;
            tx.send(res).await.unwrap();
        }
    });

    while let Some(res) = rx.recv().await {
        println!("{}", res);
    }
}

#[tokio::main]
async fn main() {
    multi_producer_single_receiver().await;
}

Send messages from multiple producers to one of multiple consumers

async-channel async-channel-crates.io async-channel-github async-channel-lib.rs cat-asynchronous cat-concurrency

async-channel offers two kinds of async multi-producer multi-consumer channel, where each message can be received by only one of all existing consumers.

  • Bounded channel with limited capacity,
  • Unbounded channel with unlimited capacity.

The Sender and Receiver sides are cloneable and can be shared among multiple threads.

When all Senders or all Receivers are dropped, the channel becomes closed. When a channel is closed, no more messages can be sent, but remaining messages can still be received. The channel can also be closed manually by calling Sender::close() or Receiver::close().

use async_channel::Receiver;
use async_channel::Sender;
use async_channel::TryRecvError;
use async_channel::bounded;
use rand::Rng;
use tokio::task;
use tokio::time;
use tokio::time::Duration;

async fn producer(id: usize, tx: Sender<String>) {
    for i in 0..5 {
        let msg = format!("Producer {}: Message {}", id, i);
        // Sends messages to the channel.
        // It creates messages in a loop, sends them to the channel.
        // If the channel is full, this method awaits until there is space for a
        // message.
        if let Err(err) = tx.send(msg).await {
            // The channel is closed.
            eprintln!("Failed to send message: {}", err);
            break;
        }
        // Simulate work
        let sleep_duration = rand::thread_rng().gen_range(10..50);
        time::sleep(Duration::from_millis(sleep_duration)).await;
    }
}

async fn consumer(id: usize, rx: Receiver<String>) {
    // Receives a message from the channel.
    // If the channel is empty, awaits until there is a message.
    // If the channel is closed, receives a message or returns an error if there
    // are no more messages.
    while let Ok(msg) = rx.recv().await {
        println!("Consumer {}: Received {}", id, msg);
        // Simulate processing
        let sleep_duration = rand::thread_rng().gen_range(30..100);
        time::sleep(Duration::from_millis(sleep_duration)).await;
    }
    assert_eq!(rx.try_recv(), Err(TryRecvError::Closed));
}

#[tokio::main]
async fn main() {
    let (tx, rx) = bounded(2); // Create a bounded channel with a capacity of 2
    // You may also use an unbounded queue.
    assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));

    // Create 3 producer tasks
    let mut producer_tasks = vec![];
    for i in 0..3 {
        let tx = tx.clone();
        producer_tasks.push(task::spawn(producer(i, tx)));
    }
    assert_eq!(tx.sender_count(), 4);

    // Create 2 consumer tasks
    let mut consumer_tasks = vec![];
    for i in 0..2 {
        let rx = rx.clone();
        consumer_tasks.push(task::spawn(consumer(i, rx)));
    }
    assert_eq!(rx.receiver_count(), 3);

    for task in producer_tasks {
        let _ = task.await;
    }
    println!(
        "The current number of messages in the channel is {}",
        tx.len()
    );

    // Close the channel to signal consumers that no more messages will be sent
    drop(tx);
    // or tx.close();
    assert!(rx.is_closed());

    for task in consumer_tasks {
        let _ = task.await;
    }
    // The channel is empty.
    assert!(rx.is_empty());
}

Broadcast messages from multiple producers to multiple consumers

postage postage-crates.io postage-github postage-lib.rs

postage is a feature-rich, portable async channel library, with different options than Tokio. postage::broadcast provides a lossless MPMC channel, which all receivers are guaranteed to receive each message.

use postage::broadcast;
use postage::prelude::Stream;
use postage::sink::Sink;
use tokio::task;
use tokio::time::Duration;

async fn broadcaster(id: usize, mut tx: broadcast::Sender<String>) {
    for i in 0..2 {
        let msg = format!("Broadcaster {}'s message {}", id, i);
        if let Err(err) = tx.send(msg.clone()).await {
            // `send` returns Err(SendError(value))
            // if the sink rejected the message.
            eprintln!("Failed to send message: {}", err);
            break;
        }
        println!("Sent: {}", msg);
        // Simulate work
        tokio::time::sleep(Duration::from_millis(10)).await;
    }
}

async fn receiver(name: &'static str, mut rx: broadcast::Receiver<String>) {
    while let Some(msg) = rx.recv().await {
        println!("{} receive {}", name, msg);
    }
}

#[tokio::main]
async fn main() {
    // The broadcast channel provides reliable broadcast delivery between
    // multiple senders and multiple receivers. The channel has a fixed
    // capacity, and senders are suspended if the buffer is filled.
    let (tx, rx) = broadcast::channel(10);

    let mut broadcaster_tasks = vec![];
    for i in 0..2 {
        let tx = tx.clone();
        broadcaster_tasks.push(task::spawn(broadcaster(i, tx)));
    }

    // Let's create a couple of receivers:
    let rx2 = rx.clone();
    task::spawn(receiver("A", rx));
    task::spawn(receiver("B", rx2));

    tokio::time::sleep(Duration::from_millis(50)).await;

    // We may also subscribe to the channel.
    // The receiver will observe all messages sent _after the call to
    // subscribe_. Messages currently in the buffer will not be received.
    let rx3 = tx.subscribe();
    task::spawn(receiver("C", rx3));
    let mut tx2 = tx.clone();
    tx2.send("Last message".into()).await.ok();

    // Wait for all the receivers to print
    tokio::time::sleep(Duration::from_millis(25)).await;
}

Streams

RecipeCratesCategories
Streamsfuturescat-asynchronous

futures cat-asynchronous

Futures are about a single value that will eventually be produced, but many event sources naturally produce a futures::stream::Stream of values over time.

use futures::Stream;
use futures::stream;
use futures::stream::StreamExt;

async fn count_to_five() -> impl Stream<Item = u32> {
    stream::iter(1..=5)
}

#[tokio::main]
async fn main() {
    let mut stream = count_to_five().await;
    // `for` loops are not usable with Streams, but for imperative-style
    // code, `while let` and the `next`/`try_next` functions can be used:
    while let Some(num) = stream.next().await {
        println!("{}", num);
    }
}

There are combinator-style methods such as futures::prelude::stream::StreamExt::map⮳, futures::prelude::stream::StreamExt::filter⮳, and futures::prelude::stream::StreamExt::fold⮳, and their early-exit-on-error cousins futures::prelude::stream::TryStreamExt::try_filter⮳, and futures::prelude::stream::TryStreamExt::try_fold⮳.

To process multiple items from a stream concurrently, use the futures::prelude::stream::StreamExt::for_each_concurrent⮳ and futures::prelude::stream::TryStreamExt::try_for_each_concurrent⮳ methods:

use std::fs;

use futures::StreamExt;
use tokio::fs::File;
use tokio::io;

type Result = std::result::Result<(), anyhow::Error>;

async fn download_file(url: &str, filename: &str) -> Result {
    let response = reqwest::get(url).await?;
    let content = response.bytes().await?;
    let mut file = File::create(filename).await?;
    io::copy(&mut content.as_ref(), &mut file).await?;
    Ok(())
}

#[tokio::main]
async fn main() -> Result {
    let urls = ["https://www.gutenberg.org/cache/epub/43/pg43.txt"]; // add more here...
    let filenames = ["temp/file1.txt"]; // add more here...
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }

    let futures = urls
        .iter()
        .zip(filenames.iter())
        .map(|(url, filename)| download_file(url, filename));

    let fut = futures::stream::iter(futures).for_each_concurrent(
        4,
        |fut| async move {
            match fut.await {
                Err(e) => {
                    println!("Error: {}", e);
                    match e.source() {
                        Some(source) => {
                            println!("  Caused by: {}", source);
                        }
                        _ => {}
                    }
                }
                _ => {}
            }
        },
    );

    fut.await;

    println!("Downloaded files successfully!");
    Ok(())
}

See also

See also Tokio async-stream.

async-stream async_stream-github

Futures crate

futures-website futures futures-crates.io futures-github futures-lib.rs cat-asynchronous

The futures⮳ crate provides a number of core abstractions for writing asynchronous code.

In most cases, you will use this crate directly only when writing async code intended to work for multiple runtimes. Otherwise, use the utilities provided by the ecosystem of your choice - Tokio for example.

Selecting futures

futures-website futures futures-crates.io futures-github futures-lib.rs cat-asynchronous

futures::future::Select⮳ polls multiple futures and streams simultaneously, executing the branch for the future that finishes first. If multiple futures are ready, one will be pseudo-randomly selected at runtime.


use futures::{
    future::FutureExt, // for `.fuse()`
    pin_mut,
    select,
};

async fn task_one() {
    // ...
}
async fn task_two() {
    // ...
}

async fn race_tasks() {
    let t1 = task_one().fuse();
    let t2 = task_two().fuse();

    pin_mut!(t1, t2);

    select! {
        () = t1 => println!("task one completed first"),
        () = t2 => println!("task two completed first"),
    }
}

#[tokio::main]
async fn main() {
    race_tasks().await;
}

Joining futures

futures-website futures futures-crates.io futures-github futures-lib.rs cat-asynchronous

use futures::join;

async fn foo(i: u32) -> u32 {
    println!("{i}");
    i
}

#[tokio::main]
async fn main() {
    // The `join!` macro polls multiple futures simultaneously, returning
    // a tuple of all results once complete.
    assert_eq!(join!(foo(1), foo(2)), (1, 2));
    // `join!` is variadic, so you can pass any number of futures

    // `join_all` create a future which represents a collection of the
    // outputs of the futures given.
    let futures = vec![foo(1), foo(2), foo(3)];
    assert_eq!(futures::future::join_all(futures).await, [1, 2, 3]);
}

Map, then, either, flatten

futures-website futures futures-crates.io futures-github futures-lib.rs cat-asynchronous

The futures⮳ crate provides an extension trait that provides a variety of convenient adapters.

use anyhow::Result;
use futures::future::FutureExt;

#[tokio::main]
async fn main() -> Result<()> {
    let future_of_1 = async { 1 };

    // Map this future’s output to a (possibly) different type, returning
    // a new future of the resulting type.
    let new_future = future_of_1.map(|x| x + 3);

    // Chain on a computation for when a future finished, passing the
    // result of the future to the provided closure f.
    let future_of_7 = new_future.then(|x| async move { x + 3 });
    let seven = future_of_7.await;
    println!("{}", seven);
    assert_eq!(seven, 7);

    // Conditional `Either` future
    let x = 6;
    let future = if x > 10 {
        async { true }.left_future()
    } else {
        async { false }.right_future()
    };
    let not_true: bool = future.await;
    assert!(!not_true);

    // Flatten nested futures
    let nested_future = async { async { 1 } };
    let future = nested_future.flatten();
    let flat = future.await;
    println!("{flat}");
    assert_eq!(flat, 1);
    Ok(())
}

See also

futures_executor

Mixing Async and Blocking Code

Call blocking code from async code

tokio tokio-crates.io tokio-github tokio-lib.rs cat-asynchronous cat-network-programming

  • Async code should never spend a long time without reaching an .await.
  • Don't carelessly mix async code and synchronous, blocking calls like std::thread::sleep(Duration::from_secs(N));
  • If you have to block the thread because of expensive CPU-bound computation, call to a synchronous IO API, use the tokio::task::spawn_blocking⮳ function, use rayon⮳, or spawn a dedicated thread.

See Async: What is blocking? blog post⮳.

Use spawn_blocking

tokio tokio-crates.io tokio-github tokio-lib.rs cat-asynchronous cat-network-programming

Use tokio::task::spawn_blocking⮳ to run a small portion of synchronous code.

#[tokio::main]
async fn main() {
    // This is running on Tokio. We may not block here.

    let blocking_task = tokio::task::spawn_blocking(|| {
        // This is running on a thread where blocking is fine.
        println!("Inside spawn_blocking");
    });

    blocking_task.await.unwrap();
}

Use the rayon crate

rayon rayon-crates.io rayon-github rayon-lib.rs cat-asynchronous cat-concurrency

use rayon::prelude::*;

async fn parallel_sum(nums: Vec<i32>) -> i32 {
    let (tx, rx) = tokio::sync::oneshot::channel();

    // Spawn a task on rayon.
    rayon::spawn(move || {
        // Perform an expensive computation on this thread...

        // ...or compute the sum on multiple rayon threads.
        let sum = nums.par_iter().sum();

        // Send the result back to Tokio.
        let _ = tx.send(sum);
    });

    // Wait for the rayon task.
    rx.await.expect("Panic in rayon::spawn")
}

#[tokio::main]
async fn main() {
    let nums = vec![1; 1024 * 1024];
    println!("{}", parallel_sum(nums).await);
}

Spawn a dedicated thread

rayon rayon-crates.io rayon-github rayon-lib.rs cat-asynchronous cat-concurrency

If a blocking operation keeps running forever, you should run it on a dedicated thread.

async fn parallel_sum(nums: Vec<i32>) -> i32 {
    let (tx, rx) = tokio::sync::oneshot::channel();

    // Spawn a task on a dedicate thread.
    std::thread::spawn(move || {
        // Perform an expensive computation on this thread...
        let sum = nums.into_iter().sum();

        // Send the result back to Tokio.
        let _ = tx.send(sum);
    });

    // Wait for the rayon task.
    rx.await.expect("Panic in rayon::spawn")
}

#[tokio::main]
async fn main() {
    let nums = vec![1; 1024 * 1024];
    println!("{}", parallel_sum(nums).await);
}

Call async code from blocking code

Bridging with sync codetokio tokio-crates.io tokio-github tokio-lib.rs cat-asynchronous cat-network-programming

In other cases, it may be easier to structure the application as largely synchronous, with smaller or logically distinct asynchronous portions. For instance, a GUI application might want to run the GUI code on the main thread and run a Tokio runtime next to it on another thread.

Use the futures executor

futures_executor futures_executor-crates.io futures_executor-github futures_executor-lib.rs cat-asynchronous

futures_executor⮳ includes a minimal executor. The futures_executor::block_on⮳ function is useful if you want to run an async function synchronously in codebase that is mostly synchronous.

async fn do_something() {
    println!("hello, world!");
}

fn main() {
    let future = do_something();
    // Futures are lazy - nothing is happening
    // until driven to completion by .await, block_on...

    // `block_on` blocks the current thread until the provided future has
    // run to completion. Other executors provide more complex
    // behavior, like scheduling multiple futures onto the same
    // thread. See `Tokio`.
    futures::executor::block_on(future);
    // `future` is run and "hello, world!" is printed
}

Use the Tokio runtime directly

tokio tokio-crates.io tokio-github tokio-lib.rs cat-asynchronous cat-network-programming

fn main() {
    let runtime = tokio::runtime::Builder::new_multi_thread()
        .worker_threads(1)
        .enable_all()
        .build()
        .unwrap();

    let mut handles = Vec::with_capacity(10);
    for i in 0..10 {
        handles.push(runtime.spawn(my_bg_task(i)));
    }

    // Do something time-consuming while the async background tasks
    // execute.
    std::thread::sleep(std::time::Duration::from_millis(750));
    println!("Finished time-consuming task.");

    // Wait for all of them to complete.
    for handle in handles {
        // The `spawn` method returns a `JoinHandle`. A `JoinHandle` is
        // a future, so we can wait for it using `block_on`.
        runtime.block_on(handle).unwrap();
    }
}

// Example async code to execute
async fn my_bg_task(i: u64) {
    // By subtracting, the tasks with larger values of i sleep for a
    // shorter duration.
    let millis = 1000 - 50 * i;
    println!("Task {} sleeping for {} ms.", i, millis);

    tokio::time::sleep(tokio::time::Duration::from_millis(millis)).await;

    println!("Task {} stopping.", i);
}

Authentication

The process of confirming identities.

Basic Authentication

RecipeCratesCategories
Perform a basic HTTP authenticationreqwestcat-authentication cat-network-programming

Basic Authentication

RecipeCratesCategories
Perform a basic HTTP authenticationreqwestcat-authentication cat-network-programming

Perform a basic HTTP authentication

reqwest cat-network-programming cat-authentication

Uses reqwest::RequestBuilder::basic_auth to perform a basic HTTP authentication.

use std::fs::File;
use std::io::copy;

use anyhow::Result;
use tempfile::Builder;

#[tokio::main]
async fn main() -> Result<()> {
    let tmp_dir = Builder::new().prefix("example").tempdir()?;
    let target = "https://www.rust-lang.org/logos/rust-logo-512x512.png";
    let response = reqwest::get(target).await?;

    let mut dest = {
        let fname = response
            .url()
            .path_segments()
            .and_then(|segments| segments.last())
            .and_then(|name| if name.is_empty() { None } else { Some(name) })
            .unwrap_or("tmp.bin");

        println!("file to download: '{}'", fname);
        let fname = tmp_dir.path().join(fname);
        println!("will be located under: '{:?}'", fname);
        File::create(fname)?
    };
    let content = response.text().await?;
    copy(&mut content.as_bytes(), &mut dest)?;
    Ok(())
}

Command Line

Techniques to help create command line interfaces, such as argument parsers, line editing, or output coloring and formatting

RecipeCratesCategories
Build complex TUIratatuicat-command-line-interface

See also

Command Line Applications in Rust (book)

Code⮳ for Command-Line Rust (O'Reilly, 2022, ISBN 9781098109417)

Command-line argument parsing

Using clap's builder API

clap clap-examples clap-github cat-command-line-interface

This application describes the structure of its command-line interface using clap⮳'s builder style. The documentation⮳ gives two other possible ways to instantiate an application.

In the builder style, with_name is the unique identifier that value_of will use to retrieve the value passed. The clap::Arg::short⮳ and clap::Arg::long⮳ options control the flag the user will be expected to type; short flags look like -f and long flags look like --file.

use std::path::PathBuf;

use clap::Arg;
use clap::Command;
use clap::value_parser;

fn cli() -> Command {
    clap::Command::new("My Test Program")
        .bin_name("test_app")
        .version("0.1.0")
        .author("Hackerman Jones <hckrmnjones@hack.gov>")
        .about("Teaches argument parsing")
        // First possible argument: --num or -n
        .arg(Arg::new("num")
                .short('n')     // -n argument
                .long("number") // --number long-form argument
                .value_name("NUMBER") // placeholder for the argument's value in the help message / usage.
                .required(false)
                .help("Enter your favorite number"))
        // Second possible argument: --file or -f
        .arg(Arg::new("file")
                .short('f')
                .long("file")
                .value_parser(value_parser!(PathBuf))
                .help("Enter the path of a file"))
    // You can also use the arg! macro: .arg(clap::arg!(-c --config <CONFIG>
    // "Optionally sets a config file to use"))
}

fn main() {
    let matches =
        cli().get_matches_from(["test_app", "-n", "42", "--file", "README.md"]);
    // In a real program, use the following to retrieve arguments from the
    // command line: let matches = cli().get_matches();

    if let Some(num) = matches.get_one::<String>("num") {
        println!("Value for num: {num}");
    }

    if let Some(file_path) = matches.get_one::<PathBuf>("file") {
        println!("Value for file: {}", file_path.display());
    }
}

Usage information is generated by clap⮳. The usage for the example application looks like this.

My Test Program 0.1.0
Hackerman Jones <hckrmnjones@hack.gov>
Teaches argument parsing

USAGE:
  testing [OPTIONS]

FLAGS:
  -h, --help    Prints help information
  -V, --version  Prints version information

OPTIONS:
  -f, --file <file>   A cool file
  -n, --number <num>  Five less than your favorite number

We can test the application by running a command like the following.

cargo run -- -f myfile.txt -n 251

The output is:

The file passed is: myfile.txt
Your favorite number must be 256.

Using clap's derive API

clap (tutorial) (cookbook) clap examples cat-command-line-interface

use std::path::PathBuf;

use anyhow::Result;
use clap::Parser;
use clap::Subcommand;

// The struct declaring the desired command-line arguments and
// commands

// The `derive` feature flag is required (see Cargo.toml).
#[derive(Parser, Debug)]
// Reads the following attributes the from the package's `Cargo.toml`
// Alternatively, use #[command(name = "MyApp")] ...
#[command(author, version, about, long_about = None)]
// Displays Help if no arguments are provided
#[command(arg_required_else_help = true)]
pub struct Cli {
    // Positional argument example
    /// The pattern to look for (the doc comment appears in the help)
    pattern: Option<String>,

    /// Required argument example (with default value and validation)
    #[arg(default_value_t = 8080)]
    #[arg(value_parser = clap::value_parser!(u16).range(1..))]
    port: u16,

    // Named argument example: the path to the file to look into
    #[arg(short, long)]
    path: Option<PathBuf>,

    /// Count example: turn debugging information on
    #[arg(short, long, action = clap::ArgAction::Count)]
    debug: u8,

    // // Alternatively, use the
    // // `clap-verbosity-flag` crate:
    // // It adds the following flags through the entire program:
    // // -q silences output
    // // -v show warnings
    // // -vv show info
    // // -vvv show debug
    // // -vvvv show trace
    // //  By default, this will only report errors.
    // #[clap(flatten)]
    // verbose: clap_verbosity_flag::Verbosity,

    // Subcommands
    #[command(subcommand)]
    pub command: Option<Commands>,
}

// The subcommands
#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Read something
    #[command(arg_required_else_help = true)]
    Read {
        /// A boolean flag
        #[arg(short, long)]
        all: bool,

        #[arg(required = true)]
        file: Vec<PathBuf>,
    },
    /// Say something
    Tell,
}

fn main() -> Result<()> {
    // `Clap` returns a Cli struct populated from `std::env::args_os()`...
    let cli = Cli::try_parse()?;
    // You also could use `parse()`

    // The argument values we got...
    println!("Path: {:?}", cli.path);

    // Use `unwrap_or` to set defaults for optional arguments
    // (or use `default_value_t` as above).
    println!("Pattern: {:?}", cli.pattern.unwrap_or("".to_string()));

    // You can see how many times a particular flag or argument occurred
    // Note, only flags can have multiple occurrences
    match cli.debug {
        0 => println!("Debug mode is off"),
        1 => println!("Debug mode is kind of on"),
        2 => println!("Debug mode is on"),
        _ => println!("Don't be crazy"),
    }

    // // Alternatively, use the `verbose` flag, if configured above
    // env_logger::Builder::new()
    //     .filter_level(cli.verbose.log_level_filter())
    //     .init();

    // Check for the existence of subcommands
    match &cli.command {
        Some(Commands::Read { all, file }) => {
            if *all {
                println!("Read all...");
            } else {
                println!("Read just one...");
            }
            println!("{:?}", file);
        }
        Some(Commands::Tell) => {
            println!("{}", 42);
        }
        None => {}
    }
    Ok(())
}

See also

lexopt

lexopt lexopt-crates.io lexopt-github lexopt-lib.rs

Fast compile times, fast runtime, pedantic about correctness. API is less ergonomic

fn main() {
    todo!();
}

pico-args

Fast compile times, fast runtime, more lax about correctness. API is more ergonomic

pico-args pico-args-crates.io pico-args-github pico-args-lib.rs

fn main() {
    todo!();
}

ANSI Terminal

ansi_term cat-command-line-interface

This program depicts the use of ansi_term⮳ crate and how it is used for controlling colours and formatting, such as blue bold text or yellow underlined text, on ANSI terminals.

There are two main data structures in ansi_term⮳: ansi_term::ANSIString⮳ and Style⮳. A Style holds stylistic information: colors, whether the text should be bold, or blinking, or whatever. There are also Colour variants that represent simple foreground colour styles. An ansi_term::ANSIString⮳ is a string paired with a ansi_term::Style⮳.

Note: British English uses Colour instead of Color.

Print colored text to the terminal

use ansi_term::Colour;

fn main() {
    println!(
        "This is {} in color, {} in color and {} in color",
        Colour::Red.paint("red"),
        Colour::Blue.paint("blue"),
        Colour::Green.paint("green")
    );
}

Print bold text to the terminal

cat-command-line-interface

For anything more complex than plain foreground color changes, the code needs to construct ansi_term::Style⮳ struct. ansi_term::Style::new⮳ creates the struct, and properties chained.

use ansi_term::Style;

fn main() {
    println!(
        "{} and this is not",
        Style::new().bold().paint("This is Bold")
    );
}

Print bold and colored text to the terminal

cat-command-line-interface

ansi_term::Color⮳ implements many similar functions as ansi_term::Style⮳ and can chain methods.

use ansi_term::Colour;
use ansi_term::Style;

fn main() {
    println!(
        "{}, {} and {}",
        Colour::Yellow.paint("This is colored"),
        Style::new().bold().paint("this is bold"),
        Colour::Yellow.bold().paint("this is bold and colored")
    );
}

Manipulate the cursor, style the output, handle input events

crossterm crossterm-crates.io crossterm-github crossterm-lib.rs

Low-level cross-platform terminal rendering and event handling.

Crossterm is a pure-rust, terminal manipulation library that makes it possible to write cross-platform text-based interfaces. It supports all UNIX and Windows terminals down to Windows 7

  • Full control over writing and flushing output buffer
  • Is tty
  • Cursor manipulation
  • Styled output
  • Terminal handling
  • Events (key inputs, mouse...)

Terminal User Interfaces

RecipeCratesCategories
Build complex TUIratatuicat-command-line-interface

Build complex TUI

ratatui ratatui-crates.io ratatui-github ratatui-lib.rs

ratatui⮳ is a lightweight, high-level library that provides a set of widgets, layouts, and utilities to build complex Rust TUIs.

fn main() {
    todo!();
}

See also

tui tui-crates.io tui-github tui-lib.rs

A library to build rich terminal user interfaces or dashboards

r3bl_tuify r3bl_tuify-crates.ioblog-tuify

User interaction

Ask for confirmation, selection, text input

inquire inquire-crates.io inquire-github inquire-lib.rs cat-command-line-interface cat-value-formatting

inquire provides several different prompts in order to interactively ask the user for information via the CLI.

use inquire::Confirm;
use inquire::Select;
use inquire::Text;

// The `inquire`` crate is used for creating interactive CLI prompts.
fn main() {
    let ans = Confirm::new("Do you want to proceed?")
        .with_default(false)
        .prompt()
        .expect("Failed to read input");
    println!("You answered: {:?}", ans);

    // Prompt for the user's name
    let name = Text::new("What's your name?")
        .prompt()
        .expect("Failed to read input");

    // Prompt for the user's favorite programming language
    let languages = vec!["Rust", "Python", "JavaScript", "C++", "Go", "Other"];
    let favorite_language = Select::new("What's your favorite programming language?", languages)
        //.with_help_message("Hint: it is Rust!")
        .prompt()
        .expect("Failed to read input");

    // Display the collected information
    println!("Hello, {}!", name);
    println!(
        "Your favorite programming language is {}.",
        favorite_language
    );
}

Display progress bars and spinners

indicatif indicatif-crates.io indicatif-github indicatif-lib.rs cat-command-line-interface

indicatif⮳ is a Rust library for indicating progress in command line applications to users.

This currently primarily provides progress bars and spinners as well as basic color support.

use std::thread;
use std::time::Duration;

use console::Emoji;
use console::Style;
use console::Term;
use indicatif::ProgressBar;
use indicatif::ProgressStyle;

fn spinner() -> anyhow::Result<()> {
    // `console` is a library for Rust that provides access to
    // terminal features, like styles and colors.
    // The terminal is abstracted through the `console::Term` type.
    let term = Term::stdout();
    term.clear_screen()?;
    term.write_line("Simulate the output of a compiler:")?;
    thread::sleep(Duration::from_millis(20));
    term.clear_line()?;
    // Print with styles / colors
    println!(
        "Target: {}",
        console::style("x86_64-unknown-linux-gnu").cyan()
    );

    // `indicatif` offers progress bars and spinners.
    // Create a progress bar with an indeterminate length:
    let pb = ProgressBar::new_spinner();
    pb.set_style(
        ProgressStyle::default_spinner()
            .template("{spinner:.green} [{elapsed_precise}] {msg}")
            .expect("Failed to set progress style"),
    );
    // Simulate different stages of the compilation process
    let stages = vec![
        ("Parsing source files", 1),
        ("Type checking", 3),
        ("Optimizing code", 2),
        ("Generating bytecode", 1),
        ("Linking objects", 1),
        ("Finalizing build", 1),
    ];
    // Spawns a background thread to tick the progress bar
    pb.enable_steady_tick(Duration::from_millis(100));
    for (stage, duration) in stages {
        pb.set_message(stage);
        thread::sleep(Duration::from_secs(duration));
    }
    pb.disable_steady_tick();
    pb.finish_with_message("Compilation complete!");
    // Print with style
    let cyan = Style::new().cyan();
    println!("Executable: {}", cyan.apply_to("./target/debug/my_program"));
    // Use emojis
    println!("{} Done!", Emoji("✨", ":-)"));
    Ok(())
}

fn main() -> anyhow::Result<()> {
    spinner()?;
    Ok(())
}
use std::thread;
use std::time::Duration;

use indicatif::ProgressBar;
use indicatif::ProgressStyle;

fn progress() {
    // Create a new progress bar with a length of 100
    // This progress bar by default draws directly to stderr.
    // If a non terminal is detected,
    // the progress bar will be completely hidden.
    let pb = ProgressBar::new(100);

    // Set a custom style for the progress bar
    pb.set_style(ProgressStyle::default_bar()
        // 40 characters wide and has cyan as primary style color and blue as alternative style color.
        .template("{spinner:.green} [{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}")
        .unwrap()
        .progress_chars("=>."));

    for _ in 0..100 {
        // Update the progress bar
        pb.inc(1);
        // Simulate work by sleeping for a short duration
        thread::sleep(Duration::from_millis(50));
    }
    // Finish the progress bar
    pb.finish_with_message("done"); // or use .finish();

    // Alternatively, you could wrap an iterator with a progress bar:
    // use indicatif::ProgressIterator;
    // for _ in (0..100).progress() {
    //     thread::sleep(Duration::from_millis(50));
    // }
}

fn main() -> anyhow::Result<()> {
    progress();
    Ok(())
}

Command-line utilities written in Rust

cat-command-line-utilities

Filesystem

RecipeCratesCategories
lsdlsdcat-command-line-utilities
exaexacat-command-line-utilities
brootbrootcat-command-line-utilities
batbatcat-command-line-utilities
openopencat-command-line-utilities

Networking

RecipeCratesCategories
gpinggpingcat-command-line-utilities

Shells

RecipeCratesCategories
starshipstarshipcat-command-line-utilities
nushellnushellcat-command-line-utilities

See also

My terminal became more Rusty

File listing and display

RecipeCratesCategories
lsdlsdcat-command-line-utilities
exaexacat-command-line-utilities
brootbrootcat-command-line-utilities
batbatcat-command-line-utilities
openopencat-command-line-utilities

lsd

lsd is a rewrite of GNU ls with lots of added features like colors, icons, tree-view, additional formatting options.

apt install lsd

exa

exa-github

exa is a modern replacement for ls.

broot

broot broot-github is a new way to see and navigate directory trees.

bat

bat is a fast cat clone with syntax highlighting and Git integration. bat-github

bat README.md
# Display multiple files at once
bat src/*.rs
# Read from stdin, determine the syntax automatically
curl -s https://sh.rustup.rs | bat
# Show and highlight non-printable characters:
bat -A /etc/hosts

open

open open-crates.io open-github open-lib.rs

Opens a path or URL using the program configured on the system.

Networking

RecipeCratesCategories
gpinggpingcat-command-line-utilities

gping

Ping, but with a graph gping

apt install gping

Shells and related utilities

RecipeCratesCategories
starshipstarshipcat-command-line-utilities
nushellnushellcat-command-line-utilities

starship

starship starship-crates.io starship-github starship-lib.rs cat-command-line-utilities

starship-github is a fast, highly customizable prompt for any shell.

nushell

Nushell(github)

Compression

Algorithms for making data smaller.

tar

Working with tarballs

flate2 uses a pure-Rust implementation by default. Use feature flags to opt in to system zlib.

Decompress a tarball

flate2 tar cat-compression

Decompress (flate2::read::GzDecoder⮳) and extract (tar::Archive::unpack⮳) all files from a compressed tarball named archive.tar.gz located in the current working directory to the same location.

use std::fs::File;

use flate2::read::GzDecoder;
use tar::Archive;

pub fn main() -> Result<(), std::io::Error> {
    let path = "temp/archive.tar.gz";

    let tar_gz = File::open(path)?;
    let tar = GzDecoder::new(tar_gz);
    let mut archive = Archive::new(tar);
    archive.unpack(".")?;
    println!("archive file unpacked!");
    Ok(())
}

Compress a directory into a tarball

flate2 tar cat-compression

Compress /var/log directory into archive.tar.gz.

Creates a std::fs::File⮳ wrapped in flate2::write::GzEncoder⮳ and tar::Builder

Adds contents of /var/log directory recursively into the archive under backup/logspath with tar::Builder::append_dir_all⮳. flate2::write::GzEncoder⮳ is responsible for transparently compressing the data prior to writing it into archive.tar.gz.


use std::fs;
use std::fs::File;

use flate2::Compression;
use flate2::write::GzEncoder;

pub fn main() -> Result<(), std::io::Error> {
    // Create a temporary folder
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    // Create the archive file
    let tar_gz_file = File::create("temp/archive.tar.gz")?;
    // Create the compression encoder
    let enc = GzEncoder::new(tar_gz_file, Compression::default());
    // Create a new archive builder with the underlying file as the
    // destination of all data written.
    let mut tar = tar::Builder::new(enc);
    // Archive the /var/log directory and all of its contents
    // (recursively), renaming it in the process
    tar.append_dir_all("temp/backup/var/log", "/var/log")?;
    println!("`archive.tar.gz` file created!");
    Ok(())
}

Decompress a tarball while removing a prefix from the paths

flate2 tar cat-compression

Iterate over the tar::Archive::entries⮳. Use std::path::Path::strip_prefix⮳ to remove the specified path prefix (bundle/logs). Finally, extract the tar::Entry⮳ via tar::Entry::unpack⮳.

use std::fs::File;
use std::path::PathBuf;

use anyhow::Result;
use flate2::read::GzDecoder;
use tar::Archive;

pub fn main() -> Result<()> {
    let file = File::open("temp/archive.tar.gz")?;
    let mut archive = Archive::new(GzDecoder::new(file));
    let prefix = "bundle/logs";

    println!("Extracted the following files:");
    archive
        .entries()?
        .filter_map(|e| e.ok())
        .map(|mut entry| -> Result<PathBuf> {
            let path = entry.path()?.strip_prefix(prefix)?.to_owned();
            entry.unpack(&path)?;
            Ok(path)
        })
        .filter_map(|e| e.ok())
        .for_each(|x| println!("> {}", x.display()));

    Ok(())
}

Concurrency

cat-concurrency

This section covers concurrent programming, and specifically parallel programming.

Parallelism implies:

  • True simultaneous execution of multiple tasks on multiple cores or processors.
  • Mechanism: uses operating system threads.
  • Important for CPU-heavy computations.
  • Often requires explicit management of threads and thread pools.
  • Requires careful synchronization to prevent data races (using mechanisms like mutexes or atomics).
  • Overhead due to thread creation and switching.

Key constructs in Rust:

  • Threads are independent units of execution that can be spawned using e.g. std::thread::spawn.
  • Mutexes e.g. std::sync::Mutex protect shared data from race conditions.
  • Channels e.g. std::sync::mpsc allow threads to communicate and exchange data.

Explicit threads

RecipeCratesCategories
Use spawn, joinstd[cat-concurrency]
Use scoped threadsstdcat-concurrency

Threadpools

Multithreading with the crossbeam crate

Message passing and channels

Shared state

Concurrent data structures

Data parallelism

Send and Sync

RecipeCratesCategories
Send and Sync traitsstdcat-concurrency

See also

Rust concurrency book

Explicit threads

RecipeCratesCategories
Use spawn, joinstd[cat-concurrency]
Use scoped threadsstdcat-concurrency

Use spawn & join

std cat-concurrency

use std::thread;
use std::time::Duration;

fn main() {
    let thread_one = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    let thread_two = thread::spawn(|| { /* ... */ });
    // More stufff

    // Wait for both threads to complete.
    thread_one.join().expect("thread one panicked");
    thread_two.join().expect("thread two panicked");
}

Note: when the main thread of a Rust program completes, all spawned threads are shut down, whether or not they have finished running.

Use scoped threads

std cat-concurrency

Scoped threads

use std::error::Error;
use std::path::Path;
use std::sync::mpsc;
use std::thread;

// Our error type needs to be `Send` to be used in a channel
fn read_contents<T: AsRef<Path>>(
    file: T,
) -> Result<String, Box<dyn Error + Send>> {
    Ok(file.as_ref().to_string_lossy().into_owned())
}

fn main() {
    // To share state between threads, consider using a channel
    let (tx, rx) = mpsc::channel();

    thread::scope(|scope| {
        // Creates a “fork-join” scope
        let tx2 = tx.clone();
        scope.spawn(move || {
            println!("hello from the first scoped thread");
            let contents = read_contents("foo.txt");
            tx.send(contents).unwrap();
        });
        scope.spawn(move || {
            println!("hello from the second scoped thread");
            let contents = read_contents("bar.txt");
            tx2.send(contents).unwrap();
        });
    });
    // No join.
    // Spawned threads get joined automatically once the scope ends!

    // Receive messages from the channel
    println!("hello from the main thread");

    for received in rx {
        println!("Got: {:?}", received);
    }
}

Threadpools

Calculate the SHA256 of ISO files concurrently

threadpool num_cpus walkdir ring cat-concurrency cat-filesystem

This example calculates the SHA256 for every file with iso extension in the current directory. A threadpool generates threads equal to the number of cores present in the system found with num_cpus::get⮳. walkdir::WalkDir::new⮳ iterates the current directory and calls walkdir::WalkDir::new⮳ to perform the operations of reading and computing SHA256 hash.

use std::fs::File;
use std::io::BufReader;
use std::io::Error;
use std::io::Read;
use std::path::Path;
use std::sync::mpsc::channel;

use ring::digest::Context;
use ring::digest::Digest;
use ring::digest::SHA256;
use threadpool::ThreadPool;
use walkdir::WalkDir;

// Verify the iso extension
fn is_iso(entry: &Path) -> bool {
    matches!(entry.extension(), Some(e) if e.to_string_lossy().to_lowercase() == "iso")
}

fn compute_digest<P: AsRef<Path>>(filepath: P) -> Result<(Digest, P), Error> {
    let mut buf_reader = BufReader::new(File::open(&filepath)?);
    let mut context = Context::new(&SHA256);
    let mut buffer = [0; 1024];

    loop {
        let count = buf_reader.read(&mut buffer)?;
        if count == 0 {
            break;
        }
        context.update(&buffer[..count]);
    }

    Ok((context.finish(), filepath))
}

fn main() -> Result<(), Error> {
    let pool = ThreadPool::new(num_cpus::get());

    let (tx, rx) = channel();

    for entry in WalkDir::new("/home/user/Downloads")
        .follow_links(true)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| !e.path().is_dir() && is_iso(e.path()))
    {
        let path = entry.path().to_owned();
        let tx = tx.clone();
        pool.execute(move || {
            let digest = compute_digest(path);
            tx.send(digest).expect("Could not send data!");
        });
    }

    drop(tx);
    for t in rx.iter() {
        let (sha, path) = t?;
        println!("{:?} {:?}", sha, path);
    }
    Ok(())
}

Draw a fractal, dispatching work to a thread pool

threadpool num num_cpus image cat-concurrency cat-science cat-rendering

This example generates an image by drawing a fractal from the Julia set⮳ with a thread pool for distributed computation.

julia-set

Allocate memory for output image of given width and height with image::ImageBuffer::new⮳. image::Rgb::from_channels⮳ calculates RGB pixel values. Create threadpool::ThreadPool⮳ with thread count equal to number of cores with num_cpus::get⮳. threadpool::ThreadPool::execute⮳ receives each pixel as a separate job.

std::sync::mpsc::channel⮳ receives the jobs and std::sync::mpsc::Receiver::recv⮳ retrieves them. image::ImageBuffer::put_pixel⮳ uses the data to set the pixel color. image::ImageBuffer::save⮳ writes the image to output.png.

use std::fs;
use std::sync::mpsc::channel;

use anyhow::Result;
use image::ImageBuffer;
use image::Rgb;
use num::complex::Complex;
use threadpool::ThreadPool;

// Function converting intensity values to RGB
fn wavelength_to_rgb(wavelength: u32) -> Rgb<u8> {
    let wave = wavelength as f32;
    let (r, g, b) = match wavelength {
        380..=439 => ((440. - wave) / (440. - 380.), 0.0, 1.0),
        440..=489 => (0.0, (wave - 440.) / (490. - 440.), 1.0),
        490..=509 => (0.0, 1.0, (510. - wave) / (510. - 490.)),
        510..=579 => ((wave - 510.) / (580. - 510.), 1.0, 0.0),
        580..=644 => (1.0, (645. - wave) / (645. - 580.), 0.0),
        645..=780 => (1.0, 0.0, 0.0),
        _ => (0.0, 0.0, 0.0),
    };
    let factor = match wavelength {
        380..=419 => 0.3 + 0.7 * (wave - 380.) / (420. - 380.),
        701..=780 => 0.3 + 0.7 * (780. - wave) / (780. - 700.),
        _ => 1.0,
    };
    let (r, g, b) = (
        normalize(r, factor),
        normalize(g, factor),
        normalize(b, factor),
    );
    Rgb([r, g, b])
}
// Maps Julia set distance estimation to intensity values
fn julia(
    c: Complex<f32>,
    x: u32,
    y: u32,
    width: u32,
    height: u32,
    max_iter: u32,
) -> u32 {
    let width = width as f32;
    let height = height as f32;
    let mut z = Complex {
        // scale and translate the point to image coordinates
        re: 3.0 * (x as f32 - 0.5 * width) / width,
        im: 2.0 * (y as f32 - 0.5 * height) / height,
    };
    let mut i = 0;
    for t in 0..max_iter {
        if z.norm() >= 2.0 {
            break;
        }
        z = z * z + c;
        i = t;
    }
    i
}
// Normalizes color intensity values within RGB range
fn normalize(color: f32, factor: f32) -> u8 {
    ((color * factor).powf(0.8) * 255.) as u8
}

fn main() -> Result<()> {
    let (width, height) = (1920, 1080);
    let mut img = ImageBuffer::new(width, height);
    let iterations = 300;

    let c = Complex::new(-0.8, 0.156);

    let pool = ThreadPool::new(num_cpus::get());
    let (tx, rx) = channel();

    for y in 0..height {
        let tx = tx.clone();
        pool.execute(move || {
            for x in 0..width {
                let i = julia(c, x, y, width, height, iterations);
                let pixel = wavelength_to_rgb(380 + i * 400 / iterations);
                tx.send((x, y, pixel)).expect("Could not send data!");
            }
        });
    }

    for _ in 0..(width * height) {
        let (x, y, pixel) = rx.recv()?;
        img.put_pixel(x, y, pixel);
    }
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    img.save("temp/output.png")?;
    println!("Image saved!");
    Ok(())
}

Multithreading with the crossbeam crate

Spawn a short-lived thread

crossbeam crossbeam-crates.io crossbeam-github crossbeam-lib.rs cat-concurrency cat-data-structures cat-memory-management cat-no-std

The example uses the crossbeam⮳ crate, which provides data structures and functions for concurrent and parallel programming. crossbeam::thread::Scope::spawn⮳ spawns a new scoped thread that is guaranteed to terminate before returning from the closure that passed into crossbeam::scope⮳ function, meaning that you can reference data from the calling function.

This example splits the array in half and performs the work in separate threads.

fn main() {
    let arr = &[1, 25, -4, 10];
    let max = find_max(arr);
    assert_eq!(max, Some(25));
    println!("The maximum is {:?}", max);
}

fn find_max(arr: &[i32]) -> Option<i32> {
    const THRESHOLD: usize = 2;

    if arr.len() <= THRESHOLD {
        return arr.iter().cloned().max();
    }

    let mid = arr.len() / 2;
    let (left, right) = arr.split_at(mid);

    crossbeam::scope(|s| {
        let thread_l = s.spawn(|_| find_max(left));
        let thread_r = s.spawn(|_| find_max(right));

        let max_l = thread_l.join().unwrap()?;
        let max_r = thread_r.join().unwrap()?;

        Some(max_l.max(max_r))
    })
    .unwrap()
}

Create a parallel pipeline

crossbeam crossbeam-crates.io crossbeam-github crossbeam-lib.rs cat-concurrency cat-data-structures cat-memory-management cat-no-std

crossbeam-channel-website crossbeam-channel crossbeam-channel-crates.io crossbeam-channel-github crossbeam-channel-lib.rs cat-algorithms cat-concurrency cat-data-structures

This example uses the crossbeam⮳ and crossbeam-channel⮳ crates to create a parallel pipeline, similar to that described in the ZeroMQ guide⮳. There is a data source and a data sink, with data being processed by two worker threads in parallel on its way from the source to the sink.

We use bounded channels with a capacity of one using crossbeam_channel::bounded⮳. The producer must be on its own thread because it produces messages faster than the workers can process them (since they sleep for half a second) - this means the producer blocks on the call to crossbeam_channel::Sender::send⮳ for half a second until one of the workers processes the data in the channel. Also note that the data in the channel is consumed by whichever worker calls receive first, so each message is delivered to a single worker rather than both workers.

Reading from the channels via the iterator crossbeam_channel::Receiver::iter⮳ method will block, either waiting for new messages or until the channel is closed. Because the channels were created within the crossbeam::scope⮳ we must manually close them via std::ops::Drop⮳ to prevent the entire program from blocking on the worker for-loops. You can think of the calls to std::ops::Drop⮳ as signaling that no more messages will be sent.

extern crate crossbeam;
extern crate crossbeam_channel;

use std::thread;
use std::time::Duration;

use crossbeam_channel::bounded;

fn main() {
    let (snd1, rcv1) = bounded(1);
    let (snd2, rcv2) = bounded(1);
    let n_msgs = 4;
    let n_workers = 2;

    crossbeam::scope(|s| {
        // Producer thread
        s.spawn(|_| {
            for i in 0..n_msgs {
                snd1.send(i).unwrap();
                println!("Source sent {}", i);
            }
            // Close the channel - this is necessary to exit
            // the for-loop in the worker
            drop(snd1);
        });

        // Parallel processing by 2 threads
        for _ in 0..n_workers {
            // Send to sink, receive from source
            let (sendr, recvr) = (snd2.clone(), rcv1.clone());
            // Spawn workers in separate threads
            s.spawn(move |_| {
                thread::sleep(Duration::from_millis(500));
                // Receive until channel closes
                for msg in recvr.iter() {
                    println!(
                        "Worker {:?} received {}.",
                        thread::current().id(),
                        msg
                    );
                    sendr.send(msg * 2).unwrap();
                }
            });
        }
        // Close the channel, otherwise sink will never
        // exit the for-loop
        drop(snd2);

        // Sink
        for msg in rcv2.iter() {
            println!("Sink received {}", msg);
        }
    })
    .unwrap();
}

Pass data between two threads

crossbeam crossbeam-crates.io crossbeam-github crossbeam-lib.rs cat-concurrency cat-data-structures cat-memory-management cat-no-std

crossbeam-channel-website crossbeam-channel crossbeam-channel-crates.io crossbeam-channel-github crossbeam-channel-lib.rs cat-algorithms cat-concurrency cat-data-structures

This example demonstrates the use of crossbeam_channel⮳ in a single producer, single consumer (SPSC) setting. We build off the crossbeam spawn⮳ example by using crossbeam::scope⮳ and crossbeam::thread::Scope::spawn⮳ to manage the producer thread. Data is exchanged between the two threads using a crossbeam::scope⮳ channel, meaning there is no limit to the number of storable messages. The producer thread sleeps for half a second in between messages.

use std::thread;
use std::time;

use crossbeam_channel::unbounded;

fn main() {
    let (snd, rcv) = unbounded();
    let n_msgs = 5;
    crossbeam::scope(|s| {
        s.spawn(|_| {
            for i in 0..n_msgs {
                snd.send(i).unwrap();
                thread::sleep(time::Duration::from_millis(100));
            }
        });
    })
    .unwrap();
    for _ in 0..n_msgs {
        let msg = rcv.recv().unwrap();
        println!("Received {}", msg);
    }
}

Parallel Tasks

rayon

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

Simple work-stealing parallelism for Rust.

Iterate in parallel

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

Convert .iter() or iter_mut() or into_iter() into par_iter() or par_iter_mut() or into_par_iter() to execute in parallel.

use rayon::prelude::*;

fn sum_of_squares(input: &[i32]) -> i32 {
    input.par_iter().map(|i| i * i).sum()
}

fn increment_all(input: &mut [i32]) {
    input.par_iter_mut().for_each(|p| *p += 1);
}

fn main() {
    let mut v = [1, 2, 3];
    increment_all(&mut v[..]);
    println!("{}", sum_of_squares(&v[..]));
}

Sort in parallel

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

use rayon::prelude::*;

fn main() {
    let mut v = [-5, 4, 1, -3, 2];
    v.par_sort();
    println!("{:#?}", v);
}

Implement custom parallel tasks

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

Rayon implements rayon::join⮳, rayon::join⮳, rayon::spawn⮳ that may run on the global or a custom Rayon threadpool⮳.

fn main() {
    // Build the threadpool
    let pool = rayon::ThreadPoolBuilder::new()
        .num_threads(8)
        .build()
        .unwrap();
    // `install` executes the closure within the threadpool. Any attempts
    // to use join, scope, or parallel iterators will then operate
    // within that threadpool.
    let n = pool.install(|| fib(20));
    println!("{}", n);
}

fn fib(n: usize) -> usize {
    if n == 0 || n == 1 {
        return n;
    }
    // Conceptually, calling join() is similar to spawning two threads,
    // one executing each of the two closures.
    let (a, b) = rayon::join(|| fib(n - 1), || fib(n - 2)); // runs inside of `pool`
    a + b
}

Mutate the elements of an array in parallel

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

The example uses the rayon⮳ crate, which is a data parallelism library for Rust. rayon⮳ provides the rayon::iter::IntoParallelRefIterator::par_iter_mut⮳ method for any parallel iterable data type. This is an iterator-like chain that potentially executes in parallel.

use rayon::prelude::*;

fn main() {
    let mut arr = [0, 7, 9, 11];
    arr.par_iter_mut().for_each(|p| *p -= 1);
    println!("{:?}", arr);
}

Test in parallel if any or all elements of a collection match a given predicate

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

This example demonstrates using the rayon::iter::ParallelIterator::any⮳ and rayon::iter::ParallelIterator::any⮳ methods, which are parallelized counterparts to std::iter::Iterator::any⮳ and std::iter::Iterator::all⮳. rayon::iter::ParallelIterator::any⮳ checks in parallel whether any element of the iterator matches the predicate, and returns as soon as one is found. rayon::iter::ParallelIterator::any⮳ checks in parallel whether all elements of the iterator match the predicate, and returns as soon as a non-matching element is found.

use rayon::prelude::*;

fn main() {
    let mut vec = vec![2, 4, 6, 8];

    assert!(!vec.par_iter().any(|n| (*n % 2) != 0));
    assert!(vec.par_iter().all(|n| (*n % 2) == 0));
    assert!(!vec.par_iter().any(|n| *n > 8));
    assert!(vec.par_iter().all(|n| *n <= 8));

    vec.push(9);

    assert!(vec.par_iter().any(|n| (*n % 2) != 0));
    assert!(!vec.par_iter().all(|n| (*n % 2) == 0));
    assert!(vec.par_iter().any(|n| *n > 8));
    assert!(!vec.par_iter().all(|n| *n <= 8));

    println!("{:?}", vec);
}

Search items using a given predicate in parallel

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

This example uses rayon::iter::ParallelIterator::find_any⮳ and rayon::iter::ParallelIterator::find_any⮳ to search a vector in parallel for an element satisfying the predicate in the given closure.

If there are multiple elements satisfying the predicate defined in the closure argument of rayon::iter::ParallelIterator::find_anyrayon⮳ returns the first one found, not necessarily the first one.

Also note that the argument to the closure is a reference to a reference (&&x). See the discussion on std::iter::Iterator::find⮳ for additional details.

use rayon::prelude::*;

fn main() {
    let v = vec![6, 2, 1, 9, 3, 8, 11];

    let f1 = v.par_iter().find_any(|&&x| x == 9);
    let f2 = v.par_iter().find_any(|&&x| x % 2 == 0 && x > 6);
    let f3 = v.par_iter().find_any(|&&x| x > 8);

    assert_eq!(f1, Some(&9));
    assert_eq!(f2, Some(&8));
    assert!(f3 > Some(&8));
    println!("{:?}", v);
}

Sort a vector in parallel

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

This example will sort in parallel a vector of Strings.

Allocate a vector of empty Strings. par_iter_mut().for_each populates random values in parallel. Although multiple options⮳ exist to sort an enumerable data type, rayon::slice::ParallelSliceMut::par_sort_unstable⮳ is usually faster than stable sort ⮳ algorithms.

use rand::Rng;
use rand::distributions::Alphanumeric;
use rand::thread_rng;
use rayon::prelude::*;

fn main() {
    let mut vec = vec![String::new(); 100];

    vec.par_iter_mut().for_each(|p| {
        let mut rng = thread_rng();
        *p = (0..5).map(|_| rng.sample(Alphanumeric) as char).collect();
    });
    vec.par_sort_unstable();
    println!("{:?}", vec);
}

Map-reduce in parallel

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency

This example uses rayon::iter::ParallelIterator::filterrayon::iter::ParallelIterator::map⮳ and rayon::iter::ParallelIterator::reduce⮳ to calculate the average age of Person objects whose age is over 30.

rayon::iter::ParallelIterator::filter⮳ returns elements from a collection that satisfy the given predicate. rayon::iter::ParallelIterator::map⮳ performs an operation on every element, creating a new iteration, and rayon::iter::ParallelIterator::reduce⮳ performs an operation given the previous reduction and the current element. Also shows use of rayon::iter::ParallelIterator::sum⮳ which has the same result as the reduce operation in this example.

use rayon::prelude::*;

struct Person {
    age: u32,
}

fn main() {
    let v: Vec<Person> = vec![
        Person { age: 23 },
        Person { age: 19 },
        Person { age: 42 },
        Person { age: 17 },
        Person { age: 17 },
        Person { age: 31 },
        Person { age: 30 },
    ];

    let num_over_30 = v.par_iter().filter(|&x| x.age > 30).count() as f32;
    let sum_over_30 = v
        .par_iter()
        .map(|x| x.age)
        .filter(|&x| x > 30)
        .reduce(|| 0, |x, y| x + y);

    let alt_sum_30: u32 = v.par_iter().map(|x| x.age).filter(|&x| x > 30).sum();

    let avg_over_30 = sum_over_30 as f32 / num_over_30;
    let alt_avg_over_30 = alt_sum_30 as f32 / num_over_30;

    assert!((avg_over_30 - alt_avg_over_30).abs() < f32::EPSILON);
    println!("The average age of people older than 30 is {}", avg_over_30);
}

Generate JPEG thumbnails in parallel

rayon rayon-crates.io rayon-github rayon-lib.rs cat-concurrency glob image cat-concurrency cat-filesystem

This example generates thumbnails for all jpg files in the current directory then saves them in a new folder called thumbnails.

glob::glob_with::glob_with⮳ finds jpeg files in current directory. rayon resizes images in parallel using rayon::iter::IntoParallelRefIterator::par_iter⮳ calling image::DynamicImage::resize

use std::fs::create_dir_all;
use std::path::Path;

use anyhow::Context;
use anyhow::Result;
use glob::MatchOptions;
use glob::glob_with;
use image::imageops::FilterType;
use rayon::prelude::*;

fn main() -> Result<()> {
    let options: MatchOptions = Default::default();
    let files: Vec<_> = glob_with("*.jpg", options)?
        .filter_map(|x| x.ok())
        .collect();

    if files.is_empty() {
        println!("No .jpg files found in current directory");
        return Ok(());
    }

    let thumb_dir = "thumbnails";
    create_dir_all(thumb_dir)?;

    println!("Saving {} thumbnails into '{}'...", files.len(), thumb_dir);

    let image_failures: Vec<_> = files
        .par_iter()
        .map(|path| {
            make_thumbnail(path, thumb_dir, 300)
                .with_context(|| path.display().to_string())
        })
        .filter_map(|x| x.err())
        .collect();

    image_failures.iter().for_each(|x| println!("{}", x));

    println!(
        "{} thumbnails saved successfully",
        files.len() - image_failures.len()
    );
    Ok(())
}

fn make_thumbnail<PA, PB>(
    original: PA,
    thumb_dir: PB,
    longest_edge: u32,
) -> Result<()>
where
    PA: AsRef<Path>,
    PB: AsRef<Path>,
{
    let img = image::open(original.as_ref())?;
    let file_path = thumb_dir.as_ref().join(original);

    Ok(img
        .resize(longest_edge, longest_edge, FilterType::Nearest)
        .save(file_path)?)
}

Message passing and channels

One increasingly popular approach to ensuring safe concurrency is message passing, where threads communicate by sending each other messages containing data. The Rust standard library provides channels for message passing that are safe to use in concurrent contexts.

Message passing in async⮳ programming is covered in a separate page: async channels.

Multiple producers, single consumer

std cat-concurrency

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let (tx, rx) = mpsc::channel();
    let tx2 = tx.clone();
    thread::spawn(move || {
        let vals = vec![String::from("hi"), String::from("hi again")];

        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    thread::spawn(move || {
        let vals = vec![String::from("more"), String::from("messages")];

        for val in vals {
            tx2.send(val).unwrap();
            thread::sleep(Duration::from_secs(1));
        }
    });

    while let Ok(msg) = rx.recv() {
        println!("{msg}");
    }
}

crossbeam-channel

crossbeam-channel-website crossbeam-channel crossbeam-channel-crates.io crossbeam-channel-github crossbeam-channel-lib.rs cat-algorithms cat-concurrency cat-data-structures

Multi-producer multi-consumer channels for message passing. The absolute fastest channel implementation available. Implements Go-like 'select' feature.

use std::thread;

use crossbeam_channel::RecvError;
use crossbeam_channel::TryRecvError;
use crossbeam_channel::unbounded;

fn main() {
    // Create a channel of unbounded capacity.
    let (s1, r1) = unbounded();

    // Alternatively, create a channel that can hold at most n messages at
    // a time. let (s1, r1) = bounded(5);

    // Senders and receivers can be cloned to use them to multiple
    // threads. cloning only creates a new handle to the same sending
    // or receiving side. It does not create a separate stream of
    // messages in any way
    let s2 = s1.clone();

    // Send a message into the channel.
    // Note that the cloned sender is moved into the thread.
    thread::spawn(move || s2.send("Hi!").unwrap());

    // Blocks until receiving the message from the channel.
    assert_eq!(r1.recv(), Ok("Hi!"));

    // Try receiving a message without blocking.
    // The channel is now empty
    assert_eq!(r1.try_recv(), Err(TryRecvError::Empty));

    s1.send("0").unwrap();
    // Receive all remaining messages currently in the channel
    // (non-blocking).
    let v: Vec<_> = r1.try_iter().collect();
    println!("{:?}", v);

    // When all senders or all receivers associated with a channel get
    // dropped, the channel becomes disconnected.
    s1.send("1").unwrap();
    drop(s1);

    // No more messages can be sent...
    // ERROR s1.send("2").unwrap();

    // .. but any remaining messages can still be received.
    println!("{:?}", r1.iter().collect::<Vec<_>>());
    // Note that the call to `collect` would block if the channel were not
    // disconnected.

    // There are no more messages in the channel.
    assert!(r1.is_empty());

    // After disconnection, calling `r1.recv()` does not block
    // Instead, `Err(RecvError)` is returned immediately.
    assert_eq!(r1.recv(), Err(RecvError));
}

Example using specialized channels for tickers and timeout

use std::time::Duration;
use std::time::Instant;

use crossbeam_channel::after;
use crossbeam_channel::select;
use crossbeam_channel::tick;

fn main() {
    let start = Instant::now();
    // Channel that delivers messages periodically.
    let ticker = tick(Duration::from_millis(50));
    // Channel that delivers a single message
    // after a certain duration of time.
    let timeout = after(Duration::from_secs(1));

    loop {
        // `select` wait until any one of the channels becomes ready and
        // execute it.
        select! {
            recv(ticker) -> _ => println!("elapsed: {:?}", start.elapsed()),
            recv(timeout) -> _ => break,
            // or use: default(Duration::from_millis(1000)) => break,
        }
    }
}

flume

flume flume-crates.io flume-github flume-lib.rs

Smaller and simpler than crossbeam-channel and almost as fast.

fn main() {
    todo!();
}

tokio

tokio-website tokio tokio-crates.io tokio-github tokio-lib.rs cat-asynchronous cat-network-programming

Tokio's sync module provides channels for using in async code.

fn main() {
    todo!();
}

See also

Message passing (rust book)

Shared-State Concurrency

Channels are similar to single ownership, because once you transfer a value down a channel, you should no longer use that value. Shared memory concurrency is like multiple ownership: multiple threads can access the same memory location at the same time.

The Rust standard library provides smart pointer types, such as Mutex<T> and Arc<T>, that are safe to use in concurrent contexts.

Maintain a global mutable state

lazy_static lazy_static-crates.io lazy_static-github lazy_static-lib.rs cat-memory-management cat-rust-patterns cat-no-std

Declare global state using lazy static. lazy static⮳ creates a globally available static ref which requires a std::sync::Mutex⮳ to allow mutation (also see std::sync::RwLock⮳). The std::sync::Mutex⮳ wrap ensures the state cannot be simultaneously accessed by multiple threads, preventing race conditions. A std::sync::MutexGuard⮳ must be acquired to read or mutate the value stored in a std::sync::Mutex⮳.

use std::sync::Mutex;

use anyhow::Result;
use anyhow::anyhow;
use lazy_static::lazy_static;

lazy_static! {
    static ref FRUIT: Mutex<Vec<String>> = Mutex::new(Vec::new());
}

fn insert(fruit: &str) -> Result<()> {
    let mut db = FRUIT
        .lock()
        .map_err(|_| anyhow!("Failed to acquire MutexGuard"))?;
    db.push(fruit.to_string());
    Ok(())
}

fn main() -> Result<()> {
    insert("apple")?;
    insert("orange")?;
    insert("peach")?;
    {
        let db = FRUIT
            .lock()
            .map_err(|_| anyhow!("Failed to acquire MutexGuard"))?;

        db.iter().enumerate().for_each(|(i, item)| {
            println!("{}: {}", i, item);
        });
    }
    insert("grape")?;
    Ok(())
}

Mutexes

std cat-concurrency

Allow access to data from one thread at a time.

use std::sync::Arc;
use std::sync::Mutex;
use std::thread;

fn main() {
    // We wrap Mutex in Arc to allow for multiple owners.
    // Arc<T> is safe to use in concurrent situations.
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        // `clone` is somewhat a misnomer; it creates another pointer to the
        // same Mutex, increasing the strong reference count.
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(
            move || {
                let mut num = counter.lock().unwrap();
                *num += 1;
            }, /* Releases the lock automatically when the MutexGuard
                * goes out of scope. */
        );
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}

parking_lot

parking_lot parking_lot-crates.io parking_lot-github parking_lot-lib.rs cat-concurrency

More compact and efficient implementations of the standard synchronization primitives.

parking_lot⮳ provides implementations of parking_lot::Mutex⮳, parking_lot::RwLock⮳, parking_lot::Condvar⮳ and parking_lot::Once⮳ that are smaller, faster and more flexible than those in the Rust standard library. It also provides a parking_lot::ReentrantMutex⮳ type.

std::sync::Mutex works fine, but parking_lot is faster.

use parking_lot::Once;
use parking_lot::OnceState;

// `Once` is a synchronization primitive which can be used to run a one-time
// initialization.
static INIT: Once = Once::new();

static mut VAL: usize = 0;

// This function will only call `expensive_computation` once, and will
// otherwise always return the value returned from the first invocation.
fn get_cached_val() -> usize {
    // Accessing a `static mut` is unsafe much of the time, unless we do so
    // in a synchronized fashion (e.g. write once or read all)
    unsafe {
        // The given closure will be executed if this is the first time
        // call_once has been called.
        INIT.call_once(|| {
            VAL = expensive_computation();
            println!("This is printed only once!");
        });
        // A closure has completed successfully.
        assert_eq!(INIT.state(), OnceState::Done);
        VAL
    }
}

fn expensive_computation() -> usize {
    // ...
    42
}

fn main() {
    // A closure has not been executed yet
    assert_eq!(INIT.state(), OnceState::New);
    for _ in 0..3 {
        assert_eq!(get_cached_val(), 42);
    }
}
use parking_lot::RwLock;

fn main() {
    let lock = RwLock::new(5);

    {
        println!("Many reader locks can be held at once");
        let r1 = lock.read();
        let r2 = lock.read();
        assert_eq!(*r1, 5);
        assert_eq!(*r2, 5);
        println!("Read locks are dropped at this point");
    }

    {
        println!("Only one write lock may be held, however");
        let mut w = lock.write();
        *w += 1;
        assert_eq!(*w, 6);
        println!("Write lock is dropped here");
    }
}

Atomics

std crossbeam cat-concurrency

Atomic types in std::sync::atomic⮳ provide primitive shared-memory communication between threads, and are the building blocks of other concurrent types. It defines atomic versions of a select number of primitive types, including std::sync::atomic::AtomicBool⮳, std::sync::atomic::AtomicIsize⮳, std::sync::atomic::AtomicUsize⮳, std::sync::atomic::AtomicI8⮳, std::sync::atomic::AtomicU16⮳, etc.

use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;

static GLOBAL_THREAD_COUNT: AtomicUsize = AtomicUsize::new(0);

fn main() {
    let old_thread_count = GLOBAL_THREAD_COUNT.fetch_add(1, Ordering::SeqCst);
    println!("live threads: {}", old_thread_count + 1);
}

The most common way to share an atomic variable is to put it into an std::sync::Arc⮳ (an atomically-reference-counted shared pointer).

crossbeam⮳ also offers crossbeam::atomic::AtomicCell⮳, a thread-safe mutable memory location. This type is equivalent to std::cell::Cell⮳, except it can also be shared among multiple threads.

use crossbeam_utils::atomic::AtomicCell;

fn main() {
    let a = AtomicCell::new(7);
    let v = a.into_inner();

    assert_eq!(v, 7);
    println!("{}", v);
}

arc-swap

arc-swap arc-swap-crates.io arc-swap-github arc-swap-lib.rs cat-data-structures cat-memory-management

The ArcSwap type is a container for an Arc that can be changed atomically. Semantically, it is similar to Atomic<Arc<T>> (if there was such a thing) or RwLock<Arc<T>> (but without the need for the locking). It is optimized for read-mostly scenarios, with consistent performance characteristics.

Concurrent Data Structures

dashmap

dashmap dashmap-crates.io dashmap-github dashmap-lib.rscat-algorithms cat-concurrency cat-data-structures

Fast concurrent HashMap for Rust.

dashmap⮳ is an implementation of a concurrent associative array / hashmap in Rust. dashmap⮳ tries to be a direct replacement for RwLock<HashMap<K, V>>.

use std::sync::Arc;
use std::thread;

use dashmap::DashMap;

fn main() {
    // Create a shared DashMap with an Arc
    let map: Arc<DashMap<&str, i32, _>> = Arc::new(DashMap::new());
    // or use: DashMap::with_capacity(20)

    // Create multiple threads
    let mut threads = Vec::new();
    for i in 0..4 {
        let map_clone = map.clone();
        let thread_id = i;
        threads.push(thread::spawn(move || {
            // Access and modify the map from each thread
            match thread_id {
                0 => {
                    map_clone.insert("key1", thread_id);
                    println!("Thread {} inserted key1", thread_id);
                }
                1 => {
                    map_clone.insert("key2", thread_id);
                    println!("Thread {} inserted key2", thread_id);
                }
                2 => match map_clone.get("key1") {
                    Some(value) => {
                        println!("Thread {} read key1: {}", thread_id, *value);
                    }
                    _ => {
                        println!("Thread {} couldn't find key1", thread_id);
                    }
                },
                3 => match map_clone.get_mut("key2") {
                    Some(mut value) => {
                        *value += 10;
                        println!(
                            "Thread {} incremented key2 value to {}",
                            thread_id, *value
                        );
                    }
                    _ => {
                        println!("Thread {} couldn't find key2", thread_id);
                    }
                },
                _ => panic!("Unknown thread ID"),
            }
        }));
    }

    // Wait for all threads to finish
    for thread in threads {
        thread.join().unwrap();
    }

    assert_eq!(map.remove("key1").unwrap().1, 0); // returns Option<(K, V)>

    assert!(map.contains_key("key2"));

    map.remove_if("key2", |_, val| *val == 11);

    // Access the final state of the map from the main thread
    println!("final count: {}", map.iter().count());
}

Bounded multi-producer multi-consumer queue

crossbeam-queue-website crossbeam-queue crossbeam-queue-crates.io crossbeam-queue-github crossbeam-queue-lib.rs cat-concurrency cat-data-structures cat-no-std

Concurrent queues.

use crossbeam_queue::ArrayQueue;

fn main() {
    let q = ArrayQueue::new(2);
    assert_eq!(q.push('a'), Ok(()));
    assert_eq!(q.push('b'), Ok(()));
    assert_eq!(q.push('c'), Err('c'));
    assert_eq!(q.pop(), Some('a'));
    println!("{:?}", q.pop());
}

flurry

flurry flurry-crates.io flurry-github flurry-lib.rs cat-concurrency cat-data-structures

flurry is particularly good for read-heavy workloads.

Refer to the comparative benchmarks of concurrent HashMaps⮳ as well.

fn main() {}

Send, Sync traits

RecipeCratesCategories
Send and Sync traitsstdcat-concurrency

Send and Sync traits

The Send and Sync traits are fundamental to Rust's concurrency. You can think of Send as "Exclusive access is thread-safe," and Sync as "Shared access is thread-safe."

A type is Send if it can be transferred across thread boundaries. Most types in Rust are Send by default, as long as they don't contain non-Send types.

Send allows an object to be used by two or more threads at different times. Thread 'A' can create and use an object, then send it to thread 'B', so thread 'B' can use the object while thread 'A' cannot. The Rust ownership model can be used to enforce this non-overlapping use. In other words, Send means that a type is safe to move from one thread to another. If the same type also implements Copy, it is safe to copy from one thread to another.

An important exception is Rc. By cloning, it allows data to have multiple owners. If one owner in thread 'A' could send the Rc to another thread, giving ownership to thread 'B', there could be other owners in thread 'A' that can still use the object. Since the reference count is modified non-atomically, the value of the count on the two threads may get out of sync and one thread may drop the pointed-at value while there are owners in the other thread. Therefore Rc does not implement Send.

A type is Sync if it is safe to be referenced from multiple threads simultaneously. This is trivial for immutable objects, but mutations need to be synchronized (performed in sequence with the same order being seen by all threads). This is often done using a Mutex or RwLock which allows one thread to proceed while others must wait. By enforcing a shared order of changes, these types can turn a non-Sync object into a Sync object. Another mechanism for making objects Sync is to use atomic types, which are essentially Sync primitives.

Arc is an Rc that uses an atomic type for the reference count. Hence it can be used by multiple threads without the count getting out of sync. If the data that the Arc points to is Sync, the entire object is Sync. If the data is not Sync (e.g. a mutable type), it can be made Sync using a Mutex. Hence the proliferation of Arc<Mutex<T>> types in multi-threaded Rust code.

T is Sync if and only if &T is Send.

use std::sync::Arc;
use std::sync::Mutex;
use std::thread;

fn main() {
    // Using Arc (Atomic Reference Counting) and Mutex (Mutual Exclusion)
    // to safely share data between threads.
    let data = Arc::new(Mutex::new(0));

    let mut handles = vec![];

    for _ in 0..3 {
        let data = Arc::clone(&data);
        // Create 3 threads, each of which increments the shared data by 1
        let handle = thread::spawn(move || {
            let mut num = data.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    // Wait for all threads to finish
    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *data.lock().unwrap());
}

Existing implementations of Send and Sync

TraitsTypes
Send and Syncprimitives; (T1, T2), [T; N], &[T], struct { x: T }, Arc, Vec, Box, Option (depending on underlying types); String, &str; Mutex, Atomic*...
!Send and !SyncRc, raw pointers *const T,*mut T, types from external libraries or the operating system that are not thread safe
Send and !Syncmpsc::Receiver<T>; UnsafeCell, Cell, RefCell: when a type has interior mutability, we must be sure that we mutate it from one place only, but this place can be everywhere as long as it is singular
!Send and Sync (rare)RwLockReadGuard, RwWriteGuard and MutexGuard; &mut T if T is !Send; structs which use thread-local storage and accesses that info in Drop

Implementing Send and Sync

As discussed above, Send and Sync are automatically derived traits. This means that, unlike almost every other trait, if a type is composed entirely of Send or Sync types, then it is Send or Sync.

If you want to work with non-Sync / Send types like raw pointers, you should build an abstraction on which Send and Sync can be derived.

Note that, by implementing the unsafe marker traits Send and Sync, you guarantee that your struct can be sent across threads safely. This means the usage of MyStruct must not cause data races or other thread safety issues. An incorrect implementation can cause Undefined Behavior. Caveat lector!

Configuration

cat-config

Facilitate configuration management for applications.

Configuration Management

RecipeCratesCategories
configconfigcat-config
confyconfycat-config

Environment Variables

RecipeCratesCategories
dotenvydotenvycat-config
std::envstdcat-config
envyenvycat-config

Environment variables

RecipeCratesCategories
dotenvydotenvycat-config
std::envstdcat-config
envyenvycat-config

dotenvy

dotenvy dotenvy-crates.io dotenvy-github dotenvy-lib.rs

dotenvy⮳ forks and supersedes dotenv⮳.

use std::env;

use anyhow::Result;

fn main() -> Result<()> {
    // Load environment variables from .env file.
    // Fails if .env file not found, not readable or invalid.
    // If variables with the same names already exist in the environment,
    // their values will be preserved. If multiple declarations for the
    // same environment variable exist in your .env file, the first one is
    // applied.
    dotenvy::dotenv()?;
    // OR: dotenvy::dotenv().ok();

    for (key, value) in env::vars() {
        println!("{key}: {value}");
    }

    Ok(())
}

std::env

std cat-config

To retrieve a single environment variable,

use std::env;

fn env_extract() -> String {
    let log_env_var = env::var("RUST_LOG").unwrap_or_else(|_| "debug".into());
    println!("RUST_LOG: {log_env_var}");

    let user_env_var = env::var("USER").expect("$USER is not set");
    println!("USER: {user_env_var}");

    // Inspect an environment variable at compile-time.
    // Uncomment to test.
    // let shell = env!("SHELL", "$SHELL is not set");

    let optional_value = option_env!("SHELL");

    optional_value.unwrap_or("no shell set").to_string()
}

fn main() {
    println!("SHELL: {}", env_extract());
}

Working with environment variables in Rust

envy

envy envy-crates.io envy-github envy-lib.rs cat-config

envy can deserialize environment variables into type-safe structs.

[dependencies]
envy = "0.4"
serde = { version = "1.0.216", features = ["derive"] }
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Configuration {
    port: u16,
}

fn main() {
    let c = envy::from_env::<Configuration>()
        .expect("Please provide the PORT env variable");

    let c2 = envy::prefixed("MY_APP__")
        .from_env::<Configuration>()
        .expect("Please provide MY_APP__PORT env variable");

    println!("c: {:?} c2: {:?}", c, c2);
}

See Also

dotenv

Configuration

RecipeCratesCategories
configconfigcat-config
confyconfycat-config

config

config config-crates.io config-github config-lib.rs cat-config

config⮳ is a layered configuration system for Rust applications. It reads from JSON, TOML, YAML, INI, RON, JSON5 files.

fn main() {
    todo!();
}

confy

confy confy-crates.io confy-github confy-lib.rs

use serde::Deserialize;
use serde::Serialize;

#[derive(Serialize, Deserialize, Debug)]
struct MyConfig {
    version: u8,
    api_key: String,
}

/// `MyConfig` implements `Default`
impl ::std::default::Default for MyConfig {
    fn default() -> Self {
        Self {
            version: 0,
            api_key: "".into(),
        }
    }
}

fn main() -> Result<(), confy::ConfyError> {
    let cfg: MyConfig = confy::load("my-app-name", None)?;
    // confy::store("my-app-name", None, cfg)?;
    println!("{:?}", cfg);
    Ok(())
}

Cryptography

cat-cryptography

Securing data.

Encryption

RecipeCratesCategories
Salt and hash a password with PBKDF2ring data-encodingcat-cryptography

Hashing

Hashing

Calculate the SHA-256 digest of a file

ring ring-crates.io ring-github ring-lib.rs cat-cryptography cat-no-std

data-encoding data-encoding-crates.io data-encoding-github data-encoding-lib.rs cat-encoding cat-no-std

Writes some data to a file, then calculates the SHA-256 digest::Digest⮳ of the file's contents using digest::Context

use std::fs;
use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::io::Write;

use anyhow::Result;
use data_encoding::HEXUPPER;
use ring::digest::Context;
use ring::digest::Digest;
use ring::digest::SHA256;

fn sha256_digest<R: Read>(mut reader: R) -> Result<Digest> {
    let mut context = Context::new(&SHA256);
    let mut buffer = [0; 1024];

    loop {
        let count = reader.read(&mut buffer)?;
        if count == 0 {
            break;
        }
        context.update(&buffer[..count]);
    }

    Ok(context.finish())
}

fn main() -> Result<()> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    let path = "temp/file.txt";

    let mut output = File::create(path)?;
    write!(output, "We will generate a digest of this text")?;

    let input = File::open(path)?;
    let reader = BufReader::new(input);
    let digest = sha256_digest(reader)?;

    println!("SHA-256 digest is {}", HEXUPPER.encode(digest.as_ref()));

    Ok(())
}

Sign and verify a message with a HMAC digest

ring ring-crates.io ring-github ring-lib.rs cat-cryptography cat-no-std

Uses ring::hmac⮳ to creates a ring::signature::Signature⮳ of a string then verifies the signature is correct.

use ring::error::Unspecified;
use ring::hmac;
use ring::rand;
use ring::rand::SecureRandom;

fn main() -> Result<(), Unspecified> {
    // Create a key
    let mut key_value = [0u8; 48];
    let rng = rand::SystemRandom::new();
    rng.fill(&mut key_value)?;
    let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);

    // Sign a message
    let message = "Legitimate and important message.";
    let signature = hmac::sign(&key, message.as_bytes());

    // Calculates the HMAC of data using the signing key key,
    // and verifies whether the resultant value equals the signature.
    hmac::verify(&key, message.as_bytes(), signature.as_ref())?;
    println!("Message verified.");
    Ok(())
}

Use general-purpose hashing algorithms

For more algorithms, see Rust Crypto Hashes: sha2, sha1, md-5

sha2 sha2-crates.io sha2-github sha2-lib.rs cat-cryptography cat-no-std

Pure Rust implementation of the SHA-2 hash function family, including SHA-224, SHA-256, SHA-384, and SHA-512.

fn main() {
    todo!();
}

sha1 sha1-crates.io sha1-github sha1-lib.rs cat-cryptography cat-no-std

SHA-1 hash function

fn main() {
    todo!();
}

md-5 md-5-crates.io md-5-github md-5-lib.rs cat-cryptography cat-no-std

MD5 hash function

fn main() {
    todo!();
}

Encrypt with AEAD

For more algorithms, see Rust Crypto AEADs: aes-gcm-siv, aes-gcm, chacha20poly1305

aes-gcm-siv aes-gcm-siv-crates.io aes-gcm-siv-github aes-gcm-siv-lib.rs cat-cryptography cat-no-std

Pure Rust implementation of the AES-GCM-SIV Misuse-Resistant Authenticated Encryption Cipher (RFC 8452) with optional architecture-specific hardware acceleration.

fn main() {
    todo!();
}

aes-gcm aes-gcm-crates.io aes-gcm-github aes-gcm-lib.rs cat-cryptography cat-no-std

Pure Rust implementation of the AES-GCM (Galois/Counter Mode) Authenticated Encryption with Associated Data (AEAD) Cipher with optional architecture-specific hardware acceleration.

fn main() {
    todo!();
}

Use the RSA algorithm

rsa rsa-crates.io rsa-github rsa-lib.rs cat-cryptography

Pure Rust RSA implementation.

fn main() {
    todo!();
}

Compute digital signatures

For more algorithms, see Rust Crypto Signatures:

  • ed25519. Use in conjunction with the ed25519-dalek crate.
  • ecdsa
  • dsa

ed25519-website ed25519 ed25519-crates.io ed25519-github ed25519-lib.rs cat-cryptography cat-no-std

Edwards Digital Signature Algorithm (EdDSA) over Curve25519 (as specified in RFC 8032) support library providing signature type definitions and PKCS#8 private key decoding/encoding support.

fn main() {
    todo!();
}

ed25519-dalek-website ed25519-dalek ed25519-dalek-crates.io ed25519-dalek-github ed25519-dalek-lib.rs cat-cryptography cat-no-std

Fast and efficient ed25519 EdDSA key generations, signing, and verification in pure Rust.

ecdsa-website ecdsa ecdsa-crates.io ecdsa-github ecdsa-lib.rs cat-cryptography cat-no-std

Pure Rust implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA) as specified in FIPS 186-4 (Digital Signature Standard), providing RFC6979 deterministic signatures as well as support for added entropy.

fn main() {
    todo!();
}

dsa-website dsa dsa-crates.io dsa-github dsa-lib.rs cat-cryptography cat-no-std

Pure Rust implementation of the Digital Signature Algorithm (DSA) as specified in FIPS 186-4 (Digital Signature Standard), providing RFC6979 deterministic signatures as well as support for added entropy.

fn main() {
    todo!();
}

Create certificates

For more formats, see Rust Crypto Formats.

  • der
  • pem-rfc7468
  • pkcs8
  • x509-cert

der-website der der-crates.io der-github der-lib.rs cat-cryptography cat-data-structures cat-encoding cat-parser-implementations cat-no-std

Pure Rust embedded-friendly implementation of the Distinguished Encoding Rules (DER) for Abstract Syntax Notation One (ASN.1) as described in ITU X.690 with full support for heapless no_std targets.

fn main() {
    todo!();
}

pem-rfc7468-website pem-rfc7468 pem-rfc7468-crates.io pem-rfc7468-github pem-rfc7468-lib.rs cat-cryptography cat-data-structures cat-encoding cat-parser-implementations cat-no-std

PEM Encoding (RFC 7468) for PKIX, PKCS, and CMS Structures, implementing a strict subset of the original Privacy-Enhanced Mail encoding intended specifically for use with cryptographic keys, certificates, and other messages. Provides a no_std-friendly, constant-time implementation suitable for use with cryptographic private keys.

fn main() {
    todo!();
}

pkcs8-website pkcs8 pkcs8-crates.io pkcs8-github pkcs8-lib.rs cat-cryptography cat-data-structures cat-encoding cat-parser-implementations cat-no-std

Pure Rust implementation of Public-Key Cryptography Standards (PKCS) #8: Private-Key Information Syntax Specification (RFC 5208), with additional support for PKCS#8v2 asymmetric key packages (RFC 5958).

fn main() {
    todo!();
}

x509-cert-website x509-cert x509-cert-crates.io x509-cert-github x509-cert-lib.rs cat-cryptography cat-data-structures cat-encoding cat-no-std

Pure Rust implementation of the X.509 Public Key Infrastructure Certificate format as described in RFC 5280.

fn main() {
    todo!();
}

Use TLS / SSL

rustls rustls-crates.io rustls-github rustls-lib.rs cat-cryptography cat-network-programming

Rustls is a portable pure-rust high-level implementation of TLS. Implements TLS 1.2 and higher.

fn main() {
    todo!();
}

native-tls native-tls-crates.io native-tls-github native-tls-lib.rs

A wrapper over a platform's native TLS implementation. Delegates to the system TLS implementations on windows and macOS, and uses OpenSSL on Linux.

fn main() {
    todo!();
}

Utilities

subtle

subtle-website subtle subtle-crates.io subtle-github subtle-lib.rs cat-cryptography cat-no-std

Pure-Rust traits and utilities for constant-time cryptographic implementations.

fn main() {
    todo!();
}

zeroize

zeroize zeroize-crates.io zeroize-github zeroize-lib.rs cat-cryptography cat-memory-management cat-os cat-no-std

Securely clear secrets from memory with a simple trait built on stable Rust primitives which guarantee memory is zeroed using an operation that will not bee optimized away by the compiler. Uses a portable pure Rust implementation that works everywhere, even WASM.

fn main() {
    todo!();
}

Encryption

RecipeCratesCategories
Salt and hash a password with PBKDF2ring data-encodingcat-cryptography

Salt and hash a password with PBKDF2

ring ring-crates.io ring-github ring-lib.rs data-encoding data-encoding-crates.io data-encoding-github data-encoding-lib.rs cat-cryptography cat-encoding cat-no-std

Uses ring::pbkdf2⮳ to hash a salted password using the PBKDF2 key derivation function ring::pbkdf2::derive⮳ Verifies the hash is correct with ring::pbkdf2::verify⮳ The salt is generated using ring::rand::SecureRandom::fill⮳ which fills the salt byte array with securely generated random numbers.

use std::num::NonZeroU32;

use data_encoding::HEXUPPER;
use ring::digest;
use ring::error::Unspecified;
use ring::pbkdf2;
use ring::rand;
use ring::rand::SecureRandom;

fn main() -> Result<(), Unspecified> {
    const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
    let n_iter = NonZeroU32::new(100_000).unwrap();
    let rng = rand::SystemRandom::new();

    let mut salt = [0u8; CREDENTIAL_LEN];
    rng.fill(&mut salt)?;

    let password = "Guess Me If You Can!";
    let mut pbkdf2_hash = [0u8; CREDENTIAL_LEN];
    pbkdf2::derive(
        pbkdf2::PBKDF2_HMAC_SHA512,
        n_iter,
        &salt,
        password.as_bytes(),
        &mut pbkdf2_hash,
    );
    println!("Salt: {}", HEXUPPER.encode(&salt));
    println!("PBKDF2 hash: {}", HEXUPPER.encode(&pbkdf2_hash));

    let should_succeed = pbkdf2::verify(
        pbkdf2::PBKDF2_HMAC_SHA512,
        n_iter,
        &salt,
        password.as_bytes(),
        &pbkdf2_hash,
    );
    assert!(should_succeed.is_ok());

    let wrong_password = "Definitely not the correct password";
    let should_fail = pbkdf2::verify(
        pbkdf2::PBKDF2_HMAC_SHA512,
        n_iter,
        &salt,
        wrong_password.as_bytes(),
        &pbkdf2_hash,
    );
    assert!(should_fail.is_err());

    Ok(())
}

Data Structures

cat-data-structures

Rust implementations of ways of organizing data suited for specific purposes.

Bitflags

Hashmaps and friends

Stack-allocated arrays

RecipeCratesCategories
arrayvecarrayveccat-data-structures
smallvecsmallveccat-data-structures
tinyvectinyveccat-data-structures

UUIDs

RecipeCratesCategories
Generate and parse UUIDsuuidcat-data-structures

Custom

Define and operate on a type represented as a bitfield

bitflags bitflags-crates.io bitflags-github bitflags-lib.rs cat-no-std

bitflags offers a macro to generate structures which behave like bitflags. It creates type-safe bitfield type MyFlags with help of bitflags::bitflags⮳ macro and implements elementary clear operation as well as std::fmt::Display⮳ trait for it. Subsequently, shows basic bitwise operations and formatting.

use std::fmt;

use bitflags::bitflags;

bitflags! {
    // Attributes can be applied to flags types
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    struct MyFlags: u32 {
        const FLAG_A       = 0b0000_0001;
        const FLAG_B       = 0b0000_0010;
        const FLAG_C       = 0b0000_0100;
        const FLAG_ABC     = Self::FLAG_A.bits()
                           | Self::FLAG_B.bits()
                           | Self::FLAG_C.bits();
    }
}

impl MyFlags {
    pub fn as_u64(&self) -> u64 {
        self.bits() as u64
    }
}

impl fmt::Display for MyFlags {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:032b}", self.bits())
    }
}

fn main() {
    let e1 = MyFlags::FLAG_A | MyFlags::FLAG_C;
    let e2 = MyFlags::FLAG_B | MyFlags::FLAG_C;
    assert_eq!((e1 | e2), MyFlags::FLAG_ABC);
    assert_eq!((e1 & e2), MyFlags::FLAG_C);
    assert_eq!((e1 - e2), MyFlags::FLAG_A);
    assert_eq!(!e2, MyFlags::FLAG_A);
    // Use the fmt::Display implementation above
    println!("e1: {} e2: {}", e1, e2);

    let flags = MyFlags::FLAG_ABC;
    assert_eq!(format!("{}", flags), "00000000000000000000000000000111");
    assert_eq!(format!("{:?}", MyFlags::FLAG_B), "MyFlags(FLAG_B)");
    assert_eq!(
        format!("{:?}", MyFlags::FLAG_A | MyFlags::FLAG_B),
        "MyFlags(FLAG_A | FLAG_B)"
    );
    println!("{:?}", flags);
}

flagset

flagset flagset-crates.io flagset-github flagset-lib.rs

FlagSet is a new, ergonomic approach to handling flags that combines the best of existing crates like bitflags and enumflags without their downsides.

use std::os::raw::c_int;

use flagset::flags;

flags! {
    enum FlagsA: u8 {
        Foo,
        Bar,
        Baz,
    }

    enum FlagsB: c_int {
        Foo,
        Bar,
        Baz,
    }
}

fn main() {}

Hashmap's friends

Store data in an insertion-ordered map

indexmap indexmap-crates.io indexmap-github indexmap-lib.rs cat-data-structures cat-no-std

indexmap offers a hash map that separately keeps track of insertion order and allows you to efficiently iterate over its elements in that order.

use indexmap::IndexMap;

// The indexmap crate in Rust provides a hash table
// where the keys have a consistent order of insertion,
// which is preserved when iterating.
fn main() {
    // Creating an IndexMap
    let mut map = IndexMap::new();

    // Inserting elements
    map.insert("a", 1);
    map.insert("b", 2);
    map.insert("c", 3);

    // Iterating in insertion order
    println!("Iterating over IndexMap in insertion order:");
    for (key, value) in &map {
        println!("{}: {}", key, value);
    }

    // Accessing elements by index
    if let Some((key, value)) = map.get_index(1) {
        println!("Element at index 1: {}: {}", key, value);
    }

    // Using the `entry` API
    map.entry("d").or_insert(4);
    map.entry("a").or_insert(10); // This won't change "a" because it already exists
    println!("IndexMap after using entry API:");
    for (key, value) in &map {
        println!("{}: {}", key, value);
    }
}

Store data in a multimap

multimap multimap-crates.io multimap-github multimap-lib.rs

multimap is implemented as a thin wrapper around std::collections::HashMap. It allows multiple values for a given key.

use anyhow::Result;
use crates_io_api::Category;
use crates_io_api::SyncClient;
use multimap::MultiMap;

// Calls the crates.io API client and retrieve the categories a given crate
// belongs to.
fn get_categories_for_crate(crate_name: &str) -> Result<Vec<Category>> {
    let client = SyncClient::new(
        "my-user-agent (my-contact@domain.com)",
        std::time::Duration::from_millis(1000), // Rate limit interval
    )?;
    // Retrieve the crate's information
    let crt = client.get_crate(crate_name)?;
    Ok(crt.categories)
}

fn main() -> Result<()> {
    let crate_names = vec!["toml", "config", "nom", "pest"];

    let mut m: MultiMap<String, &str> = MultiMap::new();
    for name in crate_names {
        for cat in get_categories_for_crate(name)? {
            // There can be multiple crates in the same category
            // A multimap allows multiple values for the same key
            m.insert(cat.slug, name);
        }
    }

    // Get all values for a given key
    println!(
        "List of crates in the `config` category: {:?}",
        m.get_vec("config")
    );

    // Or iterate over all keys and the key's vector
    for (cat, names) in m.iter_all() {
        println!("Category: {:?}, names: {:?}", cat, names);
    }
    Ok(())
}

slotmap

slotmap slotmap-crates.io slotmap-github slotmap-lib.rs

Use to store collections of objects that need stable, safe references but have no clear ownership otherwise, such as game entities or graph nodes.

slotmap provides three containers with persistent unique keys to access stored values, SlotMap , HopSlotMap and DenseSlotMap. Upon insertion a key is returned that can be used to later access or remove the values. Insertion, deletion and access all take O(1) time with low overhead. Two secondary maps, SecondaryMap and SparseSecondaryMap are also provided that map further objects to the keys created by one of the slot maps.

fn main() {
    todo!();
}

See also

Splay tree

Stack-allocated arrays

RecipeCratesCategories
arrayvecarrayveccat-data-structures
smallvecsmallveccat-data-structures
tinyvectinyveccat-data-structures

arrayvec

arrayvec arrayvec-crates.io arrayvec-github arrayvec-lib.rs cat-data-structures cat-no-std

Arrays that are ONLY stack-allocated with fixed capacity.

use arrayvec::ArrayVec;

// ArrayVec is a vector backed by a fixed size array.
// The capacity is of type usize but is range limited to u32::MAX
// It offers a simple API but also dereferences to a slice, so that the full
// slice API is available.
fn main() {
    let mut array = ArrayVec::<_, 2>::new();
    assert_eq!(array.capacity(), 2);
    // Push some elements into the ArrayVec
    array.push(1);
    array.push(2);
    assert!(array.is_full());
    // Trying to push beyond the capacity will result in a panic
    // ERROR: array.push(3);
    let overflow = array.try_push(3);
    assert!(overflow.is_err());
    // Access elements
    for i in 0..array.len() {
        println!("Element at index {}: {}", i, array[i]);
    }
    assert_eq!(&array[..], &[1, 2]);

    let mut array2: ArrayVec<i32, 3> = ArrayVec::from([1, 2, 3]);
    // Pop an element from the ArrayVec
    if let Some(value) = array2.pop() {
        println!("Popped value: {}", value);
    }
    assert_eq!(array2.len(), 2);
    assert!(!array2.is_empty());
}

smallvec

smallvec smallvec-crates.io smallvec-github smallvec-lib.rs cat-data-structures

Arrays that are stack-allocated with fallback to the heap if the fixed stack capacity is exceeded.

use smallvec::SmallVec;
use smallvec::smallvec;

fn main() {
    // Create a SmallVec with a small inline capacity of 4
    let mut small_vec: SmallVec<i32, 4> = SmallVec::new();
    // Push some elements into the SmallVec
    small_vec.push(1);
    small_vec.push(2);
    small_vec.push(3);
    small_vec.push(4);

    // You can also initialize it via a macro:
    let mut small_vec: SmallVec<i32, 4> = smallvec![1, 2, 3, 4];

    // Print the current state of the SmallVec
    println!("SmallVec (inline): {:?}", small_vec);

    // Push beyond the inline capacity, causing a heap allocation
    small_vec.push(5);

    // Print the state of the SmallVec after pushing beyond capacity
    println!("SmallVec (heap-allocated): {:?}", small_vec);

    // Access elements
    for i in 0..small_vec.len() {
        println!("Element at index {}: {}", i, small_vec[i]);
    }

    // Pop an element from the SmallVec
    if let Some(value) = small_vec.pop() {
        println!("Popped value: {}", value);
    }

    // Print the state of the SmallVec after popping
    println!("SmallVec after popping: {:?}", small_vec);

    // Split off the SmallVec
    let mut small_vec2 = small_vec.split_off(1);
    assert_eq!(small_vec, [1]);
    assert_eq!(small_vec2, [2, 3, 4]);

    // SmallVec points to a slice, so you can use normal slice
    // indexing and other methods to access its contents:
    small_vec2[0] = small_vec2[1] + small_vec2[2];
    small_vec2.sort();
}

tinyvec

tinyvec tinyvec-crates.io tinyvec-github tinyvec-lib.rs cat-data-structures cat-no-std

The tinyvec crate provides a way to work with vectors that can store a small number of elements inline, without heap allocation, and dynamically grow to the heap if necessary. It is in 100% safe Rust code. It's similar to smallvec but with a smaller feature set and no dependencies. tinyvec requires items to implement the Default trait.

use tinyvec::TinyVec;

fn main() {
    // Create a TinyVec with an inline capacity of 4 elements
    let mut tiny_vec: TinyVec<[i32; 4]> = TinyVec::new();

    // Push some elements into the TinyVec
    tiny_vec.push(1);
    tiny_vec.push(2);
    tiny_vec.push(3);
    tiny_vec.push(4);

    // Print the current state of the TinyVec
    println!("TinyVec (inline): {:?}", tiny_vec);

    // Push beyond the inline capacity, causing a heap allocation
    tiny_vec.push(5);

    // Print the state of the TinyVec after pushing beyond capacity
    println!("TinyVec (heap-allocated): {:?}", tiny_vec);

    // Access elements
    for i in 0..tiny_vec.len() {
        println!("Element at index {}: {}", i, tiny_vec[i]);
    }

    // Pop an element from the TinyVec
    if let Some(value) = tiny_vec.pop() {
        println!("Popped value: {}", value);
    }

    // Print the state of the TinyVec after popping
    println!("TinyVec after popping: {:?}", tiny_vec);
}

UUID

RecipeCratesCategories
Generate and parse UUIDsuuidcat-data-structures

A UUID is a unique 128-bit value, stored as 16 octets, and regularly formatted as a hex string in five groups. UUIDs are used to assign unique identifiers to entities without requiring a central allocating authority. They are particularly useful in distributed systems, though can be used in disparate areas, such as databases and network protocols.

Generate and parse UUIDs

uuid uuid-crates.io uuid-github uuid-lib.rs cat-data-structures cat-parser-implementations cat-no-std

uuid generates and parses UUIDs and implements a number of utility functions.

use uuid::Uuid;
use uuid::uuid;

fn main() {
    // Generate a new UUID (version 4)
    let my_uuid = Uuid::new_v4();
    println!("Generated UUID: {}", my_uuid);

    // Parse a UUID from a string
    let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
    match Uuid::parse_str(uuid_str) {
        Ok(parsed_uuid) => println!("Parsed UUID: {}", parsed_uuid),
        Err(e) => println!("Failed to parse UUID: {}", e),
    }
    // Use a macro
    const ID: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8");
    // Print as a URN
    println!("{}", ID.urn());

    // Compare UUIDs
    let another_uuid = Uuid::new_v4();
    if my_uuid == another_uuid {
        println!("The UUIDs are equal.");
    } else {
        println!("The UUIDs are different.");
    }
}

Databases

cat-database

Interface with database management systems.

SQLite

Postgres

Connection pools

RecipeCratesCategories
Create a connection pooldeadpoolcat-database

NoSQL and friends

RecipeCratesCategories
Connect to MongoDBmongodbcat-asynchronous cat-database cat-web-programming
Connect to Redisrediscat-database

Search

Query builders and ORMs

RecipeCratesCategories
sqlxsqlxcat-database
dieseldieselcat-database
SeaORMsea-ormcat-database
toastytoastycat-database

SQLite

rusqlite provides an API to SQLite and gives access to advanced SQlite features.

Create a SQLite database

rusqlite rusqlite-documentation cat-database

Use the rusqlite⮳ crate to open SQLite databases. See the documentation⮳ for compiling on Windows.

rusqlite::Connection::open⮳ will create the database if it doesn't already exist.

use std::fs;

use rusqlite::Connection;

pub fn main() -> anyhow::Result<()> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    let conn = Connection::open("temp/cats.db")?;

    conn.execute(
        "create table if not exists cat_colors (
                 id integer primary key,
                 name text not null unique
             )",
        (), // Empty list of parameters.
    )?;
    conn.execute(
        "create table if not exists cats (
                 id integer primary key,
                 name text not null,
                 color_id integer not null references cat_colors(id)
                 )",
        (), // Empty list of parameters.
    )?;
    println!("Tables created.");
    Ok(())
}

Insert and select data

rusqlite cat-database

rusqlite::Connection::open⮳ will open the database cats created in the earlier recipe. This recipe inserts data into cat_colors and cats tables using the rusqlite::Connection::execute⮳ method of rusqlite::Connection⮳. First, the data is inserted into the cat_colors table. After a record for a color is inserted, rusqlite::Connection::last_insert_rowid⮳ method of rusqlite::Connection⮳ is used to get id of the last color inserted. This id is used while inserting data into the cats table. Then, the select query is prepared using the rusqlite::Connection::prepare⮳ method which gives a rusqlite::Statement⮳ struct. Then, query is executed using rusqlite::Statement::query_map⮳ method of rusqlite::Statement

use std::collections::HashMap;

use rusqlite::Connection;
use rusqlite::Result;

#[derive(Debug)]
#[allow(dead_code)]
struct Cat {
    name: String,
    color: String,
}

pub fn main() -> Result<()> {
    let conn = Connection::open("temp/cats.db")?;

    let mut cat_colors = HashMap::new();
    cat_colors.insert(String::from("Blue"), vec!["Tigger", "Sammy"]);
    cat_colors.insert(String::from("Black"), vec!["Oreo", "Biscuit"]);

    for (color, catnames) in &cat_colors {
        conn.execute("INSERT INTO cat_colors (name) values (?1)", [
            &color.to_string()
        ])?;
        let last_id: String = conn.last_insert_rowid().to_string();

        for cat in catnames {
            conn.execute(
                "INSERT INTO cats (name, color_id) values (?1, ?2)",
                [&cat.to_string(), &last_id],
            )?;
        }
    }
    let mut stmt = conn.prepare(
        "SELECT c.name, cc.name from cats c
         INNER JOIN cat_colors cc
         ON cc.id = c.color_id;",
    )?;

    let cats = stmt.query_map([], |row| {
        Ok(Cat {
            name: row.get(0)?,
            color: row.get(1)?,
        })
    })?;

    for cat in cats {
        println!("Found cat {:?}", cat);
    }

    Ok(())
}

Using transactions

rusqlite cat-database

rusqlite::Connection::open⮳ will open the cats.db database from the top recipe.

Begin a transaction with rusqlite::Connection::transaction⮳ Transactions will roll back unless committed explicitly with rusqlite::Transaction::commit⮳.

In the following example, colors add to a table having a unique constraint on the color name. When an attempt to insert a duplicate color is made, the transaction rolls back.

use std::fs;

use anyhow::Result;
use rusqlite::Connection;

pub fn main() -> Result<()> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    let mut conn = Connection::open("temp/cats.db")?;

    successful_tx(&mut conn)?;
    println!("Successful transaction.");

    let res = rolled_back_tx(&mut conn);
    assert!(res.is_err());
    println!(
        "Attempt to insert the same name in a unique column fails. The transaction was rolled-back."
    );

    Ok(())
}

fn successful_tx(conn: &mut Connection) -> Result<()> {
    let tx = conn.transaction()?;
    tx.execute("delete from cat_colors", ())?;
    tx.execute("insert into cat_colors (name) values (?1)", [&"lavender"])?;
    tx.execute("insert into cat_colors (name) values (?1)", [&"blue"])?;
    tx.commit()?;
    Ok(())
}

fn rolled_back_tx(conn: &mut Connection) -> Result<()> {
    let tx = conn.transaction()?;
    tx.execute("delete from cat_colors", ())?;
    tx.execute("insert into cat_colors (name) values (?1)", [&"lavender"])?;
    tx.execute("insert into cat_colors (name) values (?1)", [&"blue"])?;
    tx.execute("insert into cat_colors (name) values (?1)", [&"lavender"])?;
    tx.commit()?;
    Ok(())
}

Working with Postgres

Create tables in a Postgres database

postgres cat-database

Use the postgres⮳ crate to create tables in a Postgres database.

postgres::Client::connect⮳ helps in connecting to an existing database. The recipe uses a URL string format with Client::connect. It assumes an existing database named library, the username is postgres and the password is postgres.

use postgres::Client;
use postgres::NoTls;

pub fn main() -> anyhow::Result<()> {
    // The connection URL is formatted as
    // postgresql://<user>:<password>@<host>/<db>, for example "postgresql:/
    // /postgres:postgres@localhost/library"
    let mut client = Client::connect(
        "postgresql://postgres:mysecretpassword@rust_howto_dev-postgres-1/library",
        NoTls,
    )?;

    client.batch_execute(
        "
        CREATE TABLE IF NOT EXISTS author (
            id              SERIAL PRIMARY KEY,
            name            VARCHAR NOT NULL,
            country         VARCHAR NOT NULL
            )
    ",
    )?;

    client.batch_execute(
        "
        CREATE TABLE IF NOT EXISTS book  (
            id              SERIAL PRIMARY KEY,
            title           VARCHAR NOT NULL,
            author_id       INTEGER NOT NULL REFERENCES author
            )
    ",
    )?;
    println!("Tables created!");
    Ok(())
}

Insert and query data

postgres cat-database

The recipe inserts data into the author table using postgres::Client::execute⮳ method of postgres::Client⮳. Then, displays the data from the author table using postgres::Client::query⮳ method of postgres::Client⮳.

use std::collections::HashMap;

use postgres::Client;
use postgres::Error;
use postgres::NoTls;

struct Author {
    _id: i32,
    name: String,
    country: String,
}

pub fn main() -> Result<(), Error> {
    // The connection URL is formatted as
    // postgresql://<user>:<password>@<host>/<db>, for example postgresql://
    // postgres:postgres@localhost/library
    let mut client = Client::connect(
        "postgresql://postgres:mysecretpassword@rust_howto_dev-postgres-1/library",
        NoTls,
    )?;

    let mut authors = HashMap::new();
    authors.insert(String::from("Chinua Achebe"), "Nigeria");
    authors.insert(String::from("Rabindranath Tagore"), "India");
    authors.insert(String::from("Anita Nair"), "India");

    for (key, value) in &authors {
        let author = Author {
            _id: 0,
            name: key.to_string(),
            country: value.to_string(),
        };

        client.execute(
            "INSERT INTO author (name, country) VALUES ($1, $2)",
            &[&author.name, &author.country],
        )?;
    }

    for row in client.query("SELECT id, name, country FROM author", &[])? {
        let author = Author {
            _id: row.get(0),
            name: row.get(1),
            country: row.get(2),
        };
        println!("Author {} is from {}", author.name, author.country);
    }

    Ok(())
}

Aggregate data

postgres cat-database csv-sample

This recipe lists the nationalities of the first 7999 artists in the database of the Museum of Modern Art⮳ in descending order.

use postgres::Client;
use postgres::Error;
use postgres::NoTls;

struct Nation {
    nationality: String,
    count: i64,
}

// https://github.com/MuseumofModernArt/collection/tree/main
pub fn main() -> Result<(), Error> {
    // The connection URL is formatted as
    // postgresql://<user>:<password>@<host>/<db>, for example postgresql://
    // postgres:postgres@127.0.0.1/moma
    let mut client = Client::connect(
        "postgresql://postgres:mysecretpassword@rust_howto_dev-postgres-1/moma",
        NoTls,
    )?;

    for row in client.query(
        "SELECT nationality, COUNT(nationality) AS count
 FROM artists GROUP BY nationality ORDER BY count DESC",
        &[],
    )? {
        let (nationality, count): (Option<String>, Option<i64>) =
            (row.get(0), row.get(1));

        if nationality.is_some() && count.is_some() {
            let nation = Nation {
                nationality: nationality.unwrap(),
                count: count.unwrap(),
            };
            println!("{} {}", nation.nationality, nation.count);
        }
    }

    Ok(())
}

tokio-postgres

tokio-postgres tokio-postgres-crates.io tokio-postgres-github tokio-postgres-lib.rs

Postgres-specific library. Performs better than SQLx.

fn main() {
    todo!();
}

cornucopia for postgres

cornucopia-website cornucopia cornucopia-crates.io cornucopia-github cornucopia-lib.rs cat-database

Generate type-checked Rust from your PostgreSQL: cornucopia-rs

Cornucopia is a tool powered by rust-postgres designed to generate type-checked Rust interfaces from your PostgreSQL queries. It works by preparing your queries against an actual database and then running an extensive validation suite on them. Once the queries are prepared and validated, Rust code is generated into a module, which can be imported and used in your project.

The basic premise is thus to:

  • Write PostgreSQL queries.
  • Use Cornucopia to generate Rust code.
  • Use the generated code in your project.
fn main() {
    todo!();
}

Connection pool

RecipeCratesCategories
Create a connection pooldeadpoolcat-database

Create a connection pool

deadpool deadpool-crates.io deadpool-github deadpool-lib.rs

deadpool is a simple async pool for connections and objects of any type.

use deadpool::managed;

#[derive(Debug)]
enum Error {
    Fail,
}

struct Server;

impl Server {
    async fn get_answer(&self) -> i32 {
        42
    }
}

struct Manager;

impl managed::Manager for Manager {
    type Error = Error;
    type Type = Server;

    async fn create(&self) -> Result<Server, Error> {
        Ok(Server)
    }

    async fn recycle(
        &self,
        _: &mut Server,
        _: &managed::Metrics,
    ) -> managed::RecycleResult<Error> {
        Ok(())
    }
}

type Pool = managed::Pool<Manager>;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mgr = Manager;
    let pool = Pool::builder(mgr).build()?;

    let conn = pool.get().await.map_err(|err| {
        anyhow::anyhow!("Could not retrieve from the Pool: {:?}", err)
    })?;
    let answer = conn.get_answer().await;
    assert_eq!(answer, 42);
    println!("The answer is {}", answer);
    Ok(())
}

Query builders and ORMs

RecipeCratesCategories
sqlxsqlxcat-database
dieseldieselcat-database
SeaORMsea-ormcat-database
toastytoastycat-database

sqlx

sqlx sqlx-github sqlx-lib.rs cat-database

sqlx⮳ is the Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, SQLite, and MSSQL.

Works with Postgres, MySQL, SQLite, and MS SQL. Supports compile time checking of queries. Async: supports both tokio and async-std.

fn main() {
    todo!();
}

SeaORM

sea-orm sea_orm-website sea_orm-cookbook cat-database

Seaography GraphQL server

Built on top of sqlx (see above). There is also a related sea-query crate that provides a query builder without full ORM functionality.

fn main() {
    todo!();
}

diesel

diesel diesel-lib.rs cat-database

Has excellent performance and takes an approach of strict compile time guarantees. The main crate is Sync only, but diesel-async provides an async connection implementation.

fn main() {
    todo!();
}

toasty

toasty toasty-crates.io toasty-github toasty-lib.rs

Toasty is an ORM for the Rust programming language that prioritizes ease-of-use. It supports both SQL datases as well as some NoSQL databases, including DynamoDB and Cassandra. Note that Toasty does not hide the database capabilities. Instead, Toasty exposes features based on the target database.

It is currently in active development and not yet published to crates.io. You can try using it directly from Github.

Using the example in the Toasty announcement blog, projects that use Toasty start by creating a schema file to define the application's data model.

model User {
  #[key]
  #[auto]
  id: Id,

  name: String,

  #[unique]
  email: String,

  todos: [Todo],

  moto: Option<String>,
}

model Todo {
  #[key]
  #[auto]
  id: Id,

  #[index]
  user_id: Id<User>,

  #[relation(key = user_id, references = id)]
  user: User,

  title: String,
}

Use the Toasty CLI tool to generate all necessary Rust code for working with this data model.

// Create a new user and give them some todos.
User::create()
  .name("John Doe")
  .email("john@example.com")
  .todo(Todo::create().title("Make pizza"))
  .todo(Todo::create().title("Finish Toasty"))
  .todo(Todo::create().title("Sleep"))
  .exec(&db)
  .await?;

// Load the user from the database
let user = User::find_by_email("john@example.com").get(&db).await?

// Load and iterate the user's todos
let mut todos = user.todos().all(&db).await.unwrap();

while let Some(todo) = todos.next().await {
  let todo = todo.unwrap();
  println!("{:#?}", todo);
}

NoSQL and friends

RecipeCratesCategories
Connect to MongoDBmongodbcat-asynchronous cat-database cat-web-programming
Connect to Redisrediscat-database

Connect to MongoDB

mongodb mongodb-crates.io mongodb-github mongodb-lib.rs cat-asynchronous cat-database cat-web-programming

This is the officially supported MongoDB Rust driver, a client side library that can be used to interact with MongoDB deployments in Rust applications. It uses the bson crate for BSON support. The driver contains a fully async API that requires tokio. The driver also has a sync API that may be enabled via feature flags.

use std::env;

use dotenvy::dotenv;
use mongodb::Client;
use mongodb::bson::doc;
use serde::Deserialize;
use serde::Serialize;

#[derive(Debug, Serialize, Deserialize)]
struct User {
    name: String,
    age: i32,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Read the .env file, if present
    dotenv().ok();

    // Example: mongodb://root:passwd@server:27017/
    let mongo_uri = env::var("MONGO_URI")?;
    let client = Client::with_uri_str(&mongo_uri).await?;
    let db = client.database("test_db");
    let collection = db.collection::<User>("users");

    let user = User {
        name: String::from("Alice"),
        age: 30,
    };
    collection.insert_one(user).await?;

    let filter = doc! { "name": "Alice" };
    if let Some(user) = collection.find_one(filter).await? {
        println!("Found user: {:?}", user);
    } else {
        println!("User not found");
    }

    Ok(())
}

Connect to Redis

redis redis-crates.io redis-github redis-lib.rs

Redis-rs is a high level redis library for Rust. It provides convenient access to all Redis functionality through a very flexible but low-level API. It uses a customizable type conversion trait so that any operation can return results in just the type you are expecting. This makes for a very pleasant development experience.

use std::env;

use anyhow::Context;
use anyhow::Result;
use redis::Commands;
use redis::Connection;

fn connect() -> Result<Connection> {
    let redis_host_name = env::var("REDIS_HOSTNAME")
        .context("missing environment variable REDIS_HOSTNAME")?;
    let redis_password = env::var("REDIS_PASSWORD").unwrap_or_default();
    // Does Redis server need a secure connection?
    let uri_scheme = match env::var("IS_TLS") {
        Ok(_) => "rediss",
        Err(_) => "redis",
    };
    // The URL format is
    // protocol=<protocol>]] For example, "redis://127.0.0.1/"
    let redis_conn_url =
        format!("{}://:{}@{}", uri_scheme, redis_password, redis_host_name);
    // `open` does not actually open a connection yet, but it does perform some
    // basic checks on the URL that might make the operation fail.
    Ok(redis::Client::open(redis_conn_url)?.get_connection()?)
}

fn fetch_an_integer() -> Result<isize> {
    let mut con = connect()?;
    // Throw away the result, just make sure it does not fail
    let _: () = con.set("my_key", 42)?;
    // Read back the key and return it. Because the return value
    // from the function is a result for integer, this will automatically
    // convert into one.
    Ok(con.get("my_key")?)
}

fn main() -> Result<()> {
    let my_int = fetch_an_integer()?;
    println!("{}", my_int);
    Ok(())
}

Search

Connect to Elasticsearch

elasticsearch elasticsearch-crates.io elasticsearch-github elasticsearch-lib.rs cat-api-bindings cat-database

fn main() {
    todo!();
}

infisearch

infisearch infisearch-crates.io infisearch-github infisearch-lib.rs cat-command-line-utilities

fn main() {
    todo!();
}

stork-search stork-search-crates.io stork-search-github stork-search-lib.rs cat-wasm

stork-search.net

fn main() {
    todo!();
}

minisearch

minisearch minisearch-crates.io minisearch-github minisearch-lib.rs

minisearch-client-side-fulltext-search-engine

fn main() {
    todo!();
}

typesense

typesense typesense-crates.io typesense-github typesense-lib.rs

fn main() {
    todo!();
}

tinysearch

tinysearch tinysearch-crates.io tinysearch-github tinysearch-lib.rs

tinysearch

A Tiny, Static, Full-Text Search Engine using Rust and WebAssembly

fn main() {
    todo!();
}

Date and Time

cat-date-and-time

Manage the complexity of dealing with the fourth dimension.

There are two key libraries:

  • chrono: a comprehensive, full-featured, yet complex date and time library,
  • time: a smaller, simpler library with limited functionality.

There is no clear answer as to which is best between time and chrono. Evaluate for yourself between these two, but both are trusted and well-maintained.

Duration and calculation

Parsing and displaying

Using the time crate

RecipeCratesCategories
Use the time cratetimecat-date-and-time cat-value-formatting cat-parser-implementations cat-no-std

Duration and Calculation

Measure the elapsed time between two code sections

std cat-date-and-time

Measures std::time::Instant::elapsed⮳ since std::time::Instant::now

Calling std::time::Instant::elapsed⮳ returns a std::time::Duration⮳ that we print at the end of the example. This method will not mutate or reset the std::time::Instant⮳ object.

use std::thread;
use std::time::Duration;
use std::time::Instant;

fn expensive_function() {
    thread::sleep(Duration::from_secs(1));
}

fn main() {
    let start = Instant::now();
    expensive_function();
    let duration = start.elapsed();

    println!("Time elapsed in expensive_function() is: {:?}", duration);
}

Perform checked date and time calculations

chrono cat-date-and-time

Calculates and displays the date and time two weeks from now using chrono::Date::checked_add_signed⮳ and the date of the day before that using chrono::Date::checked_sub_signed

The methods return None if the date and time cannot be calculated.

Escape sequences that are available for the chrono::DateTime::format⮳ can be found at chrono::format::strftime⮳.

use chrono::DateTime;
use chrono::Duration;
use chrono::TimeDelta;
use chrono::Utc;

fn day_earlier(date_time: DateTime<Utc>) -> Option<DateTime<Utc>> {
    date_time.checked_sub_signed(Duration::try_days(1).unwrap())
}

fn main() {
    let now = Utc::now();
    println!("{}", now);

    let almost_three_weeks_from_now = now
        .checked_add_signed(Duration::try_weeks(2).unwrap())
        .and_then(|in_2weeks| {
            in_2weeks.checked_add_signed(Duration::try_weeks(1).unwrap())
        })
        .and_then(day_earlier);

    match almost_three_weeks_from_now {
        Some(x) => println!("{}", x),
        None => eprintln!("Almost three weeks from now overflows!"),
    }

    match now.checked_add_signed(TimeDelta::MAX) {
        Some(x) => println!("{}", x),
        None => eprintln!(
            "We can't use chrono to tell the time for the Solar System to complete more than one full orbit around the galactic center."
        ),
    }
}

Convert a local time to another timezone

chrono cat-date-and-time

Gets the local time and displays it using chrono::offset::Local::now⮳ and then converts it to the UTC standard using the chrono::DateTime::from_utc⮳ struct method. A time is then converted using the chrono::offset::FixedOffset⮳ struct and the UTC time is then converted to UTC+8 and UTC-2.

use chrono::DateTime;
use chrono::FixedOffset;
use chrono::Local;
use chrono::TimeZone;

fn main() {
    let local_time = chrono::Local::now();
    // Separate into components
    let utc_time = local_time.naive_utc();
    let offset = *local_time.offset();
    // Serialize, pass through FFI... and recreate the `DateTime`:
    let local_time_new =
        DateTime::<Local>::from_naive_utc_and_offset(utc_time, offset);
    assert_eq!(local_time, local_time_new);

    // there is also
    let _utc_time = chrono::Utc::now();

    let china_timezone = FixedOffset::east_opt(8 * 3600).unwrap();
    let rio_timezone = FixedOffset::west_opt(2 * 3600).unwrap();
    println!("Local time now is {}", local_time);
    println!("UTC time now is {}", utc_time);
    println!(
        "Time in Hong Kong now is {}",
        china_timezone.from_utc_datetime(&utc_time)
    );
    println!(
        "Time in Rio de Janeiro now is {}",
        rio_timezone.from_utc_datetime(&utc_time)
    );
}

Parsing and Displaying

Examine the date and time

chrono cat-date-and-time

Gets the current UTC chrono::DateTime⮳ and its hour/minute/second via chrono::Timelike⮳ and its year/month/day/weekday via chrono::Datelike

use chrono::Datelike;
use chrono::Timelike;
use chrono::Utc;

fn main() {
    let now = Utc::now();

    let (is_pm, hour) = now.hour12();
    println!(
        "The current UTC time is {:02}:{:02}:{:02} {}",
        hour,
        now.minute(),
        now.second(),
        if is_pm { "PM" } else { "AM" }
    );
    println!(
        "And there have been {} seconds since midnight",
        now.num_seconds_from_midnight()
    );

    let (is_common_era, year) = now.year_ce();
    println!(
        "The current UTC date is {}-{:02}-{:02} {:?} ({})",
        year,
        now.month(),
        now.day(),
        now.weekday(),
        if is_common_era { "CE" } else { "BCE" }
    );
    println!(
        "And the Common Era began {} days ago",
        now.num_days_from_ce()
    );
}

Convert date to UNIX timestamp and vice versa

chrono cat-date-and-time

Converts a date given by chrono::naive::NaiveDate::from_ymd⮳ and chrono::naive::NaiveTime::from_hms⮳ to UNIX time stamp⮳ using chrono::naive::NaiveDateTime::timestamp

Then it calculates what was the date after one billion seconds since January 1, 1970 0:00:00 UTC, using chrono::naive::NaiveDateTime::from_timestamp⮳.

use chrono::DateTime;
use chrono::NaiveDate;
use chrono::NaiveDateTime;

fn main() {
    let date_time: NaiveDateTime = NaiveDate::from_ymd_opt(2017, 11, 12)
        .unwrap()
        .and_hms_opt(17, 33, 44)
        .unwrap();
    println!(
        "Number of seconds between 1970-01-01 00:00:00 and {} is
    {}.",
        date_time,
        date_time.and_utc().timestamp()
    );

    let date_time_after_a_billion_seconds =
        DateTime::from_timestamp(1_000_000_000, 0).unwrap();
    println!(
        "Date after a billion seconds since 1970-01-01 00:00:00 was
    {}.",
        date_time_after_a_billion_seconds
    );
}

Display formatted date and time

chrono cat-date-and-time

Gets and displays the current time in UTC using chrono::offset::Utc::now⮳.

Formats the current time in the well-known RFC 2822 format⮳ using chrono::DateTime::to_rfc2822⮳ and RFC 3339⮳ using chrono::DateTime::to_rfc3339⮳ and in a custom format using chrono::DateTime::format⮳.

use chrono::DateTime;
use chrono::Utc;

fn main() {
    let now: DateTime<Utc> = Utc::now();

    println!("UTC now is: {}", now);
    println!("UTC now in RFC 2822 is: {}", now.to_rfc2822());
    println!("UTC now in RFC 3339 is: {}", now.to_rfc3339());
    println!(
        "UTC now in a custom format is: {}",
        now.format("%a %b %e %T %Y")
    );
}

Parse string into DateTime struct

chrono cat-date-and-time

Parses a chrono::DateTime⮳ struct from strings representing the well-known RFC 2822 format⮳ and RFC 3339 format⮳, and a custom format, using chrono::DateTime::parse_from_rfc2822chrono::DateTime::parse_from_rfc2822⮳ and chrono::DateTime::parse_from_str⮳ respectively.

Escape sequences that are available for the chrono::DateTime::parse_from_str⮳ can be found at chrono::format::strftime⮳. Note that the chrono::DateTime::parse_from_str⮳ requires that such a DateTime struct must be creatable that it uniquely identifies a date and a time. For parsing dates and times without timezones use chrono::naive::NaiveDatechrono::naive::NaiveTime⮳ and chrono::naive::NaiveDateTime⮳.

use chrono::DateTime;
use chrono::NaiveDate;
use chrono::NaiveDateTime;
use chrono::NaiveTime;
use chrono::format::ParseError;

fn main() -> Result<(), ParseError> {
    let rfc2822 =
        DateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")?;
    println!("{}", rfc2822);

    let rfc3339 = DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00")?;
    println!("{}", rfc3339);

    let custom = DateTime::parse_from_str(
        "5.8.1994 8:00 am +0000",
        "%d.%m.%Y %H:%M %P %z",
    )?;
    println!("{}", custom);

    let time_only = NaiveTime::parse_from_str("23:56:04", "%H:%M:%S")?;
    println!("{}", time_only);

    let date_only = NaiveDate::parse_from_str("2015-09-05", "%Y-%m-%d")?;
    println!("{}", date_only);

    let no_timezone = NaiveDateTime::parse_from_str(
        "2015-09-05 23:56:04",
        "%Y-%m-%d %H:%M:%S",
    )?;
    println!("{}", no_timezone);

    Ok(())
}

Time

RecipeCratesCategories
Use the time cratetimecat-date-and-time cat-value-formatting cat-parser-implementations cat-no-std

Use the time crate

time-website time time-crates.io time-github time-lib.rscat-no-stdcat-date-and-timecat-parser-implementationscat-value-formatting cat-date-and-time cat-value-formatting cat-parser-implementations cat-no-std

Date and time library. Fully interoperable with the standard library. Mostly compatible with #![no_std].

fn main() {
    todo!();
}

Tools

cat-development-tools

Tools that provide developer-facing features such as testing, debugging, linting, performance profiling, autocompletion, formatting, and more.

Rust tools

Cargo

RecipeCratesCategories
Crate registriescrates.iocat-development-tools
RecipeCratesCategories
Package layoutcargocat-development-tools

Documentation

RecipeCratesCategories
Badgesshield.iocat-development-tools

Formatting

Installation

Other

RecipeCratesCategories
Verify your Rust codekani{{hi:kani}}cat-development-tools

Versioning

Cargo, the Rust Package Manager

Basic cargo usage

The Cargo bookcat-development-tools

cargo help or cargo <command> --help

cargo --version

# Create a new project. Can add --bin or --lib
cargo new hello_cargo

# Creates an executable file in target/debug/hello_cargo
cargo build
cargo build --release

# Build and run a project in one step
cargo run

# Pass arguments to the program and collect output
cargo run -- arg1 somefile.txt > output.txt

# Quickly checks your code to make sure it compiles but doesn’t produce an executable
cargo check

# Removes build artifacts
cargo clean

# Looks for tests to run in two places: in each of your src files and any tests in tests/.
cargo test

# Updates all dependencies - respect the SemVer constraints in cargo.toml
cargo update
# Updates just “regex”
cargo update -p regex

Cargo.toml and lock files

# Configure the package
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at
# <https://doc.rust-lang.org/cargo/reference/manifest.html>

[dependencies]

# Reference a crate to be pulled from `crates.io`
time = "0.1.12"
# This is equivalent to the ^0.1.12 SemVer version range.
# `cargo update time` should update to version 0.1.13 if it is the latest 0.1.z release,
# but would not update to 0.2.0

# Reference a Git repo
regex = { git = "https://github.com/rust-lang/regex.git" }

# Reference a sub-crate
# Points to folder `hello_utils`, inside of which a `Cargo.toml` and `src` folder
hello_utils = { path = "hello_utils", version = "0.1.0" }

Examples of version requirements and the versions that would be allowed with them:

1.2.3  :=  >=1.2.3, <2.0.0
1.2    :=  >=1.2.0, <2.0.0
1      :=  >=1.0.0, <2.0.0
0.2.3  :=  >=0.2.3, <0.3.0
0.2    :=  >=0.2.0, <0.3.0
0.0.3  :=  >=0.0.3, <0.0.4
0.0    :=  >=0.0.0, <0.1.0
0      :=  >=0.0.0, <1.0.0

Details in Specifying Dependencies

If you’re building a non-end product, such as a rust library that other rust packages will depend on, put Cargo.lock in your .gitignore.

If you’re building an end product, which are executable like command-line tool or an application, or a system library with crate-type of staticlib⮳ or cdylib⮳, check Cargo.lock into git.

# Add dependencies to Cargo.toml from the command line
cargo add actix-web@4.0.0

Package layout

RecipeCratesCategories
Package layoutcargocat-development-tools
.
├── Cargo.lock
├── Cargo.toml
├── src/
│   ├── lib.rs                      # The default library file is src/lib.rs.
│   ├── main.rs                     # The default executable file is src/main.rs.
│   └── bin/                        # Other executables can be placed in src/bin/,
│       ├── named-executable.rs     # even in library projects.
│       ├── another-executable.rs
│       └── multi-file-executable/
│           ├── main.rs
│           └── some_module.rs
├── benches/
│   ├── large-input.rs
│   └── multi-file-bench/
│       ├── main.rs
│       └── bench_module.rs
├── examples/
│   ├── simple.rs                   # cargo run --example simple
│   └── multi-file-example/
│       ├── main.rs
│       └── ex_module.rs
└── tests/                          # Integration tests go in the tests directory.
    ├── some-integration-tests.rs   # Tests in your src files should be unit tests
    └── multi-file-test/            # and documentation tests.
        ├── main.rs
        └── test_module.rs

If you’re building a non-end product, such as a rust library that other rust packages will depend on, put Cargo.lock in your .gitignore.

  • A package is a bundle of one or more crates - as defined by a Cargo.toml file
  • A crate is the smallest amount of code that the Rust compiler considers at a time.
  • A crate can come in one of two forms: a binary crate (must have a function called main⮳) or a library crate.
  • A package can contain as many binary crates as you like, but at most only one library crate.
  • If a package contains src/main.rs and src/lib.rs, it has two crates: a binary and a library, both with the same name as the package.

Crate Registries

RecipeCratesCategories
Crate registriescrates.iocat-development-tools

In Rust, a library or executable program is called a crate. Crates are compiled using the Rust compiler, rustc rustc⮳.

Crate registries

cat-development-tools

The Rust community’s crate registry: crates.io

Alternative to crates.io: lib.rs

Installing

Build and install a Rust binary with cargo install

cargo-website cargo cargo-crates.io cargo-github cargo-lib.rs cat-development-tools

This command manages cargo’s local set of installed binary crates. Only packages which have executable [[bin]] or [[example]] targets can be installed, and all executables are installed into the installation root’s bin folder. By default only binaries, not examples, are installed. There are multiple sources from which a crate can be installed. The default source location is crates.io, but the --git, --path, and --registry flags can change this source. This command operates on system or user level, not project level.

Install a Rust binary with cargo binstall

cargo-binstall cargo-binstall-crates.io cargo-binstall-github cargo-binstall-lib.rs cat-development-tools

cargo binstall provides a low-complexity mechanism for installing Rust binaries as an alternative to building from source (e.g. via cargo install) or manually downloading packages. This is intended to work with existing CI artifacts and infrastructure, and with minimal overhead for package maintainers.

cargo binstall works by fetching the crate information from crates.io and searching the linked repository for matching releases and artifacts, falling back to the quickinstall third-party artifact host, to alternate targets as supported, and finally to cargo install as a last resort.

Rustup

Install and manage Rust toolchains with rustup

rustup Rustup documentationcat-development-tools

rustup⮳ is a toolchain multiplexer. It installs, manages, and upgrades versions of the rust compiler rustc, the Rust package manager cargo, the Rust linter clippy, the Rust code formatter rustfmt, etc.

More precisely, rustup can install and manage multiple Rust toolchains and presents them all through a single set of tools installed to ~/.cargo/bin. The rustc⮳ and cargo⮳ executables installed e.g. in ~/.cargo/bin are proxies that delegate to the real toolchain.

rustup is similar to Python's pyenv⮳ or Node's nvm⮳.

Key rustup commands⮳ include the following:

# Rustup's help
rustup help

# Show the help page for a subcommand (like `toolchain`)
rustup toolchain help

# Show which toolchain will be used in the current directory
rustup show

# Update to a new version of Rust
rustup update

# Show which toolchain will be used in the current directory
rustup target list

# Overview of what is installed on your system
rustup toolchain list

# See a list of available and installed components.
rustup component list

Rustup also offers convenience commands:

# Open the local documentation in your browser
rustup doc
# May require `rustup component add rust-docs`

Text Editors

cat-text-editors

Applications for editing text.

Formatting and Linting

Format your Rust code with rustfmt

rustfmt_nightly-github cat-development-tools

Install with rustup component add rustfmt

rustfmt <filename e.g. lib.rs> <main.rs> ...

# or for the whole project
cargo fmt

Using --check instructs rustfmt⮳ to exit with an error code if the input is not formatted correctly (useful for CI).

cargo fmt --all -- --check

Configure rustfmt

Create a rustfmt.toml in the project root folder. For example,

edition = "2021"
style_edition = "2021"
unstable_features = true

newline_style = "Unix"
#max_width = 100 # default: 100
use_small_heuristics = "Max"
format_code_in_doc_comments = true
indent_style = "Visual"

# Imports
imports_granularity = "Item" # or "Crate" or "Module"
imports_layout = "Vertical"
group_imports = "StdExternalCrate"

# Comments
comment_width = 100
wrap_comments = true
normalize_comments = true
normalize_doc_attributes = true

# Functions
fn_params_layout = "Compressed"

# Impl
reorder_impl_items = true

# Structs
use_field_init_shorthand = true

# Macros
use_try_shorthand = true

List config options with

rustfmt --help=config

Use attributes to skip code formatting in your code

rustfmt_nightly-github cat-development-tools

For things you do not want rustfmt to mangle, use #[rustfmt::skip] , #[rustfmt::skip::macros(macro_name)] , or #![rustfmt::skip::attributes(custom_attribute)]

Documentation

Document your code

  • Add documentation comments to your code.
/// This is a doc comment
/// Note the three slashes
/// The first line is equivalent to the next line.
/// This is a doc comment
fn documented_function() {
    println!("Function with doc comment.");
}

// Alternatively, you may use an external file

#[doc = include_str!("../../../../README.md")]
fn function_including_external_file_as_documentation() {}

fn main() {
    documented_function();
    function_including_external_file_as_documentation();
}

rustdoc⮳ uses the CommonMark Markdown specification.

#![allow(dead_code)]

/// Returns a person with the name given them
///
/// # Arguments
///
/// * `name` - A string slice that holds the name of the person
///
/// # Examples
///
/// ```
/// // You can have rust code between fences inside the comments
/// // If you pass --test to `rustdoc`, it will even test it for you!
/// use doc::Person;
/// let person = Person::new("name");
/// ```
fn new(name: &str) -> Person {
    Person {
        name: name.to_string(),
    }
}

#[derive(Debug)]
struct Person {
    name: String,
}

fn main() {
    let john = new("John");
    println!("{:?}", john);
}

Any item annotated with #[doc(hidden)] will not appear in the documentation.

Run rustdoc src/lib.rs --crate-name <name> or cargo doc --open to create a new directory, doc (or target/doc when using cargo), with a website inside.

Create module- or crate-level documentation

Use //! at the top of the file (instead of ///) for module-level documentation.

The first lines within lib.rs will compose the crate-level documentation front-page.

//! Fast and easy queue abstraction.
//!
//! Provides an abstraction over a queue. When the abstraction is used
//! there are these advantages:
//! - Fast
//! - `[Easy]`
//!
//! [Easy]: http://thatwaseasy.example.com

fn main() {
    println!(
        "//! ... are `inner` comments that apply to the containing module (or crate)."
    );
}

To add a "run" button on your documentation (allowing its execution in the rust playground), use the following attribute:

#![doc(html_playground_url = "https://playground.example.com/")]

fn main() {
    println!("Note the above is an _inner_ attribute that starts with #!");
    println!("It should be place at the top of your crate.")
}

See also

The rustdoc book

docs.rs⮳: open-source documentation host for Rust crates.

mdBook

Write online books with mdBook

mdbook-github cat-development-tools

mdBook⮳ is a utility to create modern online books from Markdown files.

cargo install mdbook
mdbook serve --open

Let readers execute your sample code in the Rust playground

Rust by example - Playground cat-development-tools

Playground (Rust by example)

Augment mdbook with plugins

mdbook has a large number of third-party plugins⮳.

Check links with mdbook-linkcheck

mdbook-linkcheck mdbook-linkcheck-crates.io mdbook-linkcheck-github mdbook-linkcheck-lib.rs

mdbook-linkcheck is a backend for mdbook which will check your links for you.

Hide entire chapters with mdbook-private

mdbook-private mdbook-private-crates.io mdbook-private-github mdbook-private-lib.rs

An mdbook preprocessor that controls visibility of private chapters and sections within them

Hide pages with mdbook-hide

mdbook-hide mdbook-hide-crates.io mdbook-hide-github mdbook-hide-lib.rs cat-development-tools

A preprocessor for mdbook that adds support for hidden chapters

cargo install mdbook-hide

Deploy your book or documentation in a CD / CI pipeline

GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline.

GitHub Actions for mdBook allows you to build your site with mdbook. Linux (Ubuntu), macOS, and Windows are supported.

name: github pages

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-20.04
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v2

      - name: Setup mdBook
        uses: peaceiris/actions-mdbook@v2
        with:
          mdbook-version: '0.4.10'
          # mdbook-version: 'latest'

      - run: mdbook build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./book

Badges

RecipeCratesCategories
Badgesshield.iocat-development-tools

cat-development-tools

A badge is a small image that displays status information about a crate, for example the version number or build status, and links to a detailed page.

Shield.io

Versioning

Parse and increment a version string

semver cat-config version string

Constructs a semver::Version⮳ from a string literal using semver::Version::parse⮳ then increments it by patch, minor, and major version number one by one.

Note that in accordance with the semantic versioning specification`⮳, incrementing the minor version number resets the patch version number to 0 and incrementing the major version number resets both the minor and patch version numbers to 0.

use anyhow::Result;
use semver::Version;

fn main() -> Result<()> {
    let parsed_version = Version::parse("0.2.6")?;
    // Note: a SemVer version must have exactly three components

    assert_eq!(parsed_version, Version {
        major: 0,
        minor: 2,
        patch: 6,
        pre: semver::Prerelease::EMPTY,
        build: semver::BuildMetadata::EMPTY,
    });

    // parsed_version.increment_patch();
    // assert_eq!(parsed_version.to_string(), "0.2.7");
    // println!("New patch release: v{}", parsed_version);

    // parsed_version.increment_minor();
    // assert_eq!(parsed_version.to_string(), "0.3.0");
    // println!("New minor release: v{}", parsed_version);

    // parsed_version.increment_major();
    // assert_eq!(parsed_version.to_string(), "1.0.0");
    // println!("New major release: v{}", parsed_version);

    Ok(())
}

Parse a complex version string

semver cat-config

Constructs a semver::Version⮳ from a complex version string using semver::Version::parse⮳ The string contains pre-release and build metadata as defined in the semantic versioning specification`⮳.

Note that, in accordance with the Specification, build metadata is parsed but not considered when comparing versions. In other words, two versions may be equal even if their build strings differ.

use anyhow::Result;
use semver::BuildMetadata;
use semver::Prerelease;
use semver::Version;

fn main() -> Result<()> {
    let version_str = "1.0.49-125+g72ee7853";
    let parsed_version = Version::parse(version_str)?;

    assert_eq!(parsed_version, Version {
        major: 1,
        minor: 0,
        patch: 49,
        pre: Prerelease::new("125")?,
        build: BuildMetadata::new("g72ee7853")?,
    });

    let serialized_version = parsed_version.to_string();
    assert_eq!(&serialized_version, version_str);
    println!("{}", serialized_version);

    Ok(())
}

Check if a given version is pre-release

semver cat-config

Given two versions, semver::Version⮳ asserts that one is pre-release and the other is not.

use anyhow::Result;
use semver::Version;

fn main() -> Result<()> {
    let version_1 = Version::parse("1.0.0-alpha")?;
    println!("{:?}", version_1);
    let version_2 = Version::parse("1.0.0")?;
    println!("{:?}", version_2);

    assert!(!version_1.pre.is_empty());
    assert!(version_2.pre.is_empty());

    Ok(())
}

Find the latest version satisfying a given range

semver cat-config

Given a list of version &strs, finds the latest semver::Versionsemver::VersionReq⮳ filters the list with semver::VersionReq::matches⮳ Also demonstrates semver⮳ pre-release preferences.

use anyhow::Result;
use semver::Version;
use semver::VersionReq;

fn find_max_matching_version<'a, I>(
    version_req_str: &str,
    iterable: I,
) -> Result<Option<Version>>
where
    I: IntoIterator<Item = &'a str>,
{
    let vreq = VersionReq::parse(version_req_str)?;

    Ok(iterable
        .into_iter()
        .filter_map(|s| Version::parse(s).ok())
        .filter(|s| vreq.matches(s))
        .max())
}

fn main() -> Result<()> {
    assert_eq!(
        find_max_matching_version("<= 1.0.0", vec!["0.9.0", "1.0.0", "1.0.1"])?,
        Some(Version::parse("1.0.0")?)
    );

    assert_eq!(
        find_max_matching_version(">1.2.3-alpha.3", vec![
            "1.2.3-alpha.3",
            "1.2.3-alpha.4",
            "1.2.3-alpha.10",
            "1.2.3-beta.4",
            "3.4.5-alpha.9",
        ])?,
        Some(Version::parse("1.2.3-beta.4")?)
    );

    Ok(())
}

Check external command version for compatibility

semver cat-text-processing cat-os

Runs git --version using std::process::Command⮳ then parses the version number into a semver::Version⮳ using semver::Version::parsesemver::VersionReq::matches⮳ compares semver::VersionReq to the parsed version. The command output resembles "git version x.y.z".

use std::process::Command;

use anyhow::Context;
use anyhow::Result;
use anyhow::anyhow;
use anyhow::bail;
use semver::Version;
use semver::VersionReq;

fn main() -> Result<()> {
    let version_constraint = "> 1.12.0";
    let version_test = VersionReq::parse(version_constraint)?;
    let output = Command::new("git").arg("--version").output()?;

    if !output.status.success() {
        bail!("Command executed with failing error code");
    }

    let stdout = String::from_utf8(output.stdout)?;
    let version = stdout
        .split(" ")
        .last()
        .ok_or_else(|| anyhow!("Invalid command output"))?
        .trim(); // Remove any extraneous newlines
    let parsed_version =
        Version::parse(version).context(format!("version: {}", version))?;

    if !version_test.matches(&parsed_version) {
        bail!(
            "Command version lower than minimum supported version (found {}, need {})",
            parsed_version,
            version_constraint
        );
    }
    println!("{:?}", parsed_version);
    Ok(())
}

Others

Search for Rust APIs

Roogleroogle-github cat-development-tools

Roogle is a Rust API search engine, which allows you to search functions by names and type signatures. The query can be one of the following types:

fn f(type) -> typefn f(&mut HashMap<K, V>, K, V) -> Option
fn (type) -> typefn (&char) -> bool
fn(type) -> typefn(Option<Option>) -> Option
(type) -> type(&mut Vec, value: T)

Deploy your Rust code on shuttle.rs

shuttle.rscat-development-tools

fn main() {
    todo!();
}

Minimize Rust binary sizes

How to minimize Rust binary size

By default, Rust optimizes for execution speed, compilation speed, and ease of debugging. This approach is suitable for most applications, as it balances performance and developer productivity. However, in specific scenarios where binary size is a critical concern (e.g., embedded systems or deployment to constrained environments), Rust offers mechanisms to optimize for smaller binary sizes.

cargo build --release

Generate Rust code

Top artificial intelligence tools that can generate code to help programmers

Manage and build code

Save and run project-specific commands with the just command runner

just just-crates.io just-github just-lib.rs cat-development-tools cat-command-line-utilities

just is a command runner. It is a Rust-based equivalent to make without the ability to detect file changes, but with significantly fewer syntactic warts.

Consult the Just programmer's manual⮳.

Create a justfile

Place the following example justfile in the root folder of your project. Run just to see a list of recipes. Run just <recipe> to execute the corresponding recipe.

# Load a .env file, if present.
set dotenv-load

default:
 @just --list --unsorted

# Check a local package and all of its dependencies for errors
check:
 @cargo check

# Compile a local package and all of its dependencies
build: check
 @cargo build

# Run a binary or example of the local packagels
run: check
 @cargo run

system-info:
 @echo "This is an {{arch()}} machine".

# Shebang script example
foo:
 #!/usr/bin/env bash
 set -euxo pipefail
 hello='Yo'
 echo "$hello from Bash!"

Install just in a Dev Container

FROM mcr.microsoft.com/devcontainers/base:bullseye
# or perhaps mcr.microsoft.com/devcontainers/rust:bullseye if you want rust & cargo

SHELL ["bash", "-c"]

# Prerequisites to install Just: https://just.systems/man/en
RUN <<EOF
 wget -qO - 'https://proget.makedeb.org/debian-feeds/prebuilt-mpr.pub' | gpg --dearmor | sudo tee /usr/share/keyrings/prebuilt-mpr-archive-keyring.gpg 1> /dev/null
 echo "deb [arch=all,$(dpkg --print-architecture) signed-by=/usr/share/keyrings/prebuilt-mpr-archive-keyring.gpg] https://proget.makedeb.org prebuilt-mpr $(lsb_release -cs)" | sudo tee /etc/apt/sources.list.d/prebuilt-mpr.list
 sudo apt update
EOF

RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
  && apt-get -y install just \
  && apt-get autoremove -y && apt-get clean -y

For Alpine, use apk⮳:

RUN apk add just

Check your Rust code in the background

bacon bacon-crates.io bacon-github bacon-lib.rs cat-development-tools cat-command-line-utilities

bacon is a background rust code checker. It is designed for minimal interaction, so that you can just let it run, alongside your editor, and be notified of warnings, errors, or test failures in your Rust code.

# Install or update `bacon`
cargo install --locked bacon

# Check the current project
bacon

# Run clippy instead of cargo check
bacon clippy

Code Verification and Formal Methods

RecipeCratesCategories
Verify your Rust codekani{{hi:kani}}cat-development-tools

Rust formal methods

Formal Methods are a collection of mathematically rigorous techniques used to specify, design, and verify software and hardware systems. These methods employ formal logic and mathematical proofs to ensure the correctness and reliability of systems, especially those that are safety-critical or security-critical.

Program verification is the process of formally proving the correctness of a program. This involves analyzing the code and ensuring that it meets specific properties, such as:

  • Memory safety: The program does not have memory leaks, buffer overflows, or other memory-related errors.
  • Thread safety: The program can be executed concurrently without causing data races or other concurrency issues.
  • Functional correctness: The program produces the correct output for all valid inputs.
  • Performance: The program meets specific performance requirements, such as execution time or memory usage.

There are two main approaches to Rust program verification:

  1. Static verification: This involves analyzing the code at compile time to identify potential errors and prove the correctness of certain properties. Rust's type system and ownership model already provide a strong foundation for static verification. Additionally, tools like miri and kani can be used to perform more advanced static analysis.

  2. Dynamic verification: This involves running the program with different inputs and checking its behavior against expected results. Techniques like fuzz testing can be used to identify potential issues.

Verify your Rust code

kani kani-crates.io kani-github kani-lib.rs

kani⮳ is a Rust verifier.

fn main() {
    todo!();
}

Miri Interpreter

Detect undefined behavior with the miri interpreter

miri-github cat-development-tools

miri⮳ is an experimental interpreter for Rust's mid-level intermediate representation (MIR). It can run binaries and test suites of cargo projects and detect certain classes of undefined behavior. It can run binaries and test suites of cargo projects and detect unsafe code that fails to uphold its safety requirements. It can also perform cross-interpretation for arbitrary foreign targets.

Install the miri interpreter

rustup +nightly component add miri
cargo clean
cargo miri test
# or
cargo miri run

Build Time Tooling

cat-development-tools::build-utils

Utilities for build scripts and other build time steps.

Build time tooling

This section covers "build-time" tooling, or code that is run prior to compiling a crate's source code. Conventionally, build-time code lives in a build.rs file and is commonly referred to as a "build script". Common use cases include rust code generation and compilation of bundled C/C++/asm code. See crates.io's documentation on the matter⮳ for more information.

Compile and link statically to a bundled C library

cc cat-development-tools cat-development-tools::build-utils

To accommodate scenarios where additional C, C++, or assembly is required in a project, the cc⮳ crate offers a simple api for compiling bundled C/C++/asm code into static libraries (.a) that can be statically linked to by rustc.

The following example has some bundled C code (src/hello.c) that will be used from rust. Before compiling rust source code, the "build" file (build.rs) specified in Cargo.toml runs. Using the cc⮳ crate, a static library file will be produced (in this case, libhello.a, see cc::Build::compile⮳) which can then be used from rust by declaring the external function signatures in an compile⮳, which can then be used from rust by declaring the external function signatures in an extern block⮳ block.

Since the bundled C is very simple, only a single source file needs to be passed to cc::Build⮳. For more complex build requirements, cc::Build⮳ offers a full suite of builder methods for specifying cc::Build::include⮳ paths and extra compiler cc::Build::flags⮳.

Cargo.toml

[package]
...
build = "build.rs"

[build-dependencies]
cc = "1"

[dependencies]
error-chain = "0.11"

build.rs

fn main() {
    // cc::Build::new().file("src/hello.c").compile("hello");
    // // outputs `libhello.a`
}

src/hello.c

#include <stdio.h>

void hello() {
    printf("Hello from C!\n");
}

void greet(const char* name) {
    printf("Hello, %s!\n", name);
}

src/main.rs

use std::ffi::CString;
use std::os::raw::c_char;

use anyhow::Result;

fn prompt(s: &str) -> Result<String> {
    use std::io::Write;
    print!("{}", s);
    std::io::stdout().flush()?;
    let mut input = String::new();
    std::io::stdin().read_line(&mut input)?;
    Ok(input.trim().to_string())
}

unsafe extern "C" {
    fn hello();
    fn greet(name: *const c_char);
}

fn main() -> Result<()> {
    // unsafe { hello() }
    let name = prompt("What's your name? ")?;
    let c_name = CString::new(name)?;
    // unsafe { greet(c_name.as_ptr()) }
    Ok(())
}

Compile and link statically to a bundled C++ library

cc cat-development-tools

Linking a bundled C++ library is very similar to linking a bundled C library. The two core differences when compiling and statically linking a bundled C++ library are specifying a C++ compiler via the builder method cc::Build::cpp⮳ and preventing name mangling by the C++ compiler by adding the extern "C" section at the top of our C++ source file.

Cargo.toml (static C++)

[package]
...
build = "build.rs"

[build-dependencies]
cc = "1"

build.rs (static C++)

fn main() {
    // cc::Build::new()
    //     .cpp(true)
    //     .file("src/foo.cpp")
    //     .compile("foo");
    // println!("");
}

src/foo.cpp (static C++)

extern "C" {
    int multiply(int x, int y);
}

int multiply(int x, int y) {
    return x*y;
}

src/main.rs (static C++)

unsafe extern "C" {
    fn multiply(x: i32, y: i32) -> i32;
}

fn main() {
    // unsafe {
    //     println!("{}", multiply(5, 7));
    // }
}

Compile a C library while setting custom defines

cc cat-development-tools

It is simple to build bundled C code with custom defines using cc::Build::define⮳ The method takes an std::option::Option⮳ value, so it is possible to create defines such as #define APP_NAME "foo" as well as #define WELCOME (pass std::option::Option::None⮳ as the value for a value-less define). This example builds a bundled C file with dynamic defines set in build.rs and prints "Welcome to foo - version 1.0.2" when run. Cargo sets some environment variables⮳ which may be useful for some custom defines.

Cargo.toml (custom defines)

[package]
...
version = "1.0.2"
build = "build.rs"

[build-dependencies]
cc = "1"

build.rs (custom defines)

fn main() {
    // cc::Build::new()
    //     .define("APP_NAME", "\"foo\"")
    //     .define(
    //         "VERSION",
    //         format!("\"{}\"", env!("CARGO_PKG_VERSION")).as_str(),
    //     )
    //     .define("WELCOME", None)
    //     .file("src/foo.c")
    //     .compile("foo");
}

src/foo.c (custom defines)

#include <stdio.h>

void print_app_info() {
#ifdef WELCOME
    printf("Welcome to ");
#endif
    printf("%s - version %s\n", APP_NAME, VERSION);
}

src/main.rs (custom defines)

unsafe extern "C" {
    fn print_app_info();
}

fn main() {
    // unsafe {
    //     print_app_info();
    // }
    println!("Printed app info.");
}

Cargo Plugins

cat-development-tools::cargo-plugins

Subcommands that extend the capabilities of Cargo.

Writing code

Formatting and Linting

Building

Watching for changes

RecipeCratesCategories
cargo watchcargo-watchcat-development-tools::cargo-plugins
cargo limitcargo-limitcat-development-tools::cargo-plugins

Cross-compiling

Auditing

Performance

Maintenance

See also

  • Testing
  • Build Utils
  • Debugging
  • FFI
  • Procedural Macro Helpers
  • Profiling

Write code

Generate a Rust project from a template

cargo-generate cargo-generate-crates.io cargo-generate-github cargo-generate-lib.rs

cat-development-tools cat-development-tools::cargo-plugins

Cargo Generate⮳ is a developer tool to help you get up and running quickly with a new Rust project by leveraging a pre-existing git repository as a template.

Quickly open the crates.io or docs.rs page for the latest version of a crate

cargo-crates cargo-crates-crates.io cargo-crates-github cargo-crates-lib.rs

cargo crates is a cargo command to quickly open the crates.io or docs.rs page for the latest version of a crate.

Code formatting and linting

Format your code

rustfmt-nightly rustfmt-nightly-crates.io rustfmt-nightly-github rustfmt-nightly-lib.rs cat-development-tools cat-development-tools::cargo-plugins

# Install `rustfmt` if needed
rustup component add rustfmt

cargo fmt

# Fails if code is not formatted; use in CD / CI
cargo fmt -- --check

Lint your code

clippy clippy-crates.io clippy-github clippy-lib.rs cat-development-tools cat-development-tools::cargo-plugins

Clippy is the official Rust linter. It catches common mistakes and improves your Rust code.

rustup component add clippy # install if needed
cargo clippy

Mute a warning using the #[allow(clippy::lint_name)] attributes.

Fix compiler warnings automatically

rustfix rustfix-crates.io rustfix-github rustfix-lib.rs cat-development-tools cat-development-tools::cargo-plugins

Can automatically fix compiler warnings that have a clear way to correct the problem that’s likely what you want.

cargo fix

Format or lint your code before committing it

cargo-husky cargo-husky-crates.io cargo-husky-github cargo-husky-lib.rs cat-development-tools

cargo-husky⮳ setup Git hooks automatically for cargo projects with 🐶

Git hook scripts are useful for identifying simple issues (failing tests, trailing white spaces, formatting of the code, of JSON, and YAML files...) before committing code, prior to submission to code review.

Add the cargo-husky crate to the [dev-dependencies] section of your project's Cargo.toml.

[dev-dependencies]
cargo-husky = "1"

Then run tests in your project directory.

cargo test

See also pre-commit⮳, which is a Python framework for managing and maintaining multi-language pre-commit hooks.

Build and run

cargo make

cargo-make cargo-make-crates.io cargo-make-github cargo-make-lib.rs cat-development-tools::testing cat-development-tools cat-command-line-utilities cat-development-tools::cargo-plugins cat-development-tools::build-utils

Rust task runner and build tool. The cargo-make task runner enables to define and configure sets of tasks and run them as a flow. A task is a command, script, rust code, or other sub tasks to execute. Tasks can have dependencies which are also tasks that will be executed before the task itself. With a simple toml based configuration file, you can define a multi platform build script that can run build, test, generate documentation, run bench tests, run security validations and more, executed by running a single command.

Install with

cargo install --force cargo-make
cargo make --version

automating-your-rust-workflows-with-cargo-make

cargo xtask

cargo-xtask cargo-xtask-crates.io cargo-xtask-github cargo-xtask-lib.rs

cargo-xtask⮳ adds free-form automation to a Rust project, a-la makenpm run or bespoke bash scripts.

The two distinguishing features of xtask are the following:

  • It doesn't require any other binaries besides cargo and rustc, it fully bootstraps from them
  • Unlike bash, it can more easily be cross platform, as it doesn't use the shell.

Use devx

devx-cmd devx-cmd-crates.io devx-cmd-github devx-cmd-lib.rs devx-pre-commit devx-pre-commit-crates.io devx-pre-commit-github devx-pre-commit-lib.rs cat-development-tools

devx⮳ is a collection of utilities for writing your own dev scripts in Rust. The project is inspired by and intended for seamless usage with cargo-xtask⮳ idioms.

devx-cmd provides primitives for spawning child processes that are easier than std::process targeted when used in development scripts. devx-pre-commit creates git pre-commit hooks that enforce good practices.

Make Rust a better bash with xshell

xshell xshell-crates.io xshell-github xshell-lib.rs

xshell provides a set of cross-platform utilities for writing cross-platform and ergonomic "bash" scripts.

Cross-compilation

Cross-compile using zig as the linker

cargo-zigbuild cargo-zigbuild-crates.io cargo-zigbuild-github cargo-zigbuild-lib.rs

Compile Cargo project with zig as linker.

cargo install --locked cargo-zigbuild

Watching for changes

RecipeCratesCategories
cargo watchcargo-watchcat-development-tools::cargo-plugins
cargo limitcargo-limitcat-development-tools::cargo-plugins

cargo watch

cargo-watch cargo-watch-crates.io cargo-watch-github cargo-watch-lib.rs cat-development-tools cat-development-tools::cargo-plugins

cargo install cargo-watch

# Runs `cargo check` after every code change
cargo watch -x check

# Run cargo check after code changes.
# If it succeeds, it launches cargo test.
# If tests pass, it launches the application with cargo run.
cargo watch -x check -x test -x run

cargo limit

cargo-limit cargo-limit-crates.io cargo-limit-github cargo-limit-lib.rs

cargo-limit⮳ is Cargo with less noise: warnings are skipped until errors are fixed, Neovim integration, etc.

  • errors have highest priority
  • they never appear in the middle of warnings
  • warnings are skipped by default until errors are fixed
  • external path dependencies' warnings are skipped by default
  • all messages come in reversed order by default to avoid extra scrolling
  • messages are grouped by filenames
  • number of messages can be limited
  • after encountering first error the rest of build time is limited by default
  • files can be automatically opened in your text editor on affected lines

This tool is especially useful in combination with cargo-watch.

Performance

Configure your cargo project for maximum performance, fast compile times or minimal binary size

cargo-wizard cargo-wizard-crates.io cargo-wizard-github cargo-wizard-lib.rs cat-development-tools::cargo-plugins

cargo wizard⮳ is a cargo subcommand for configuring Cargo projects. It applies profile and config templates to your Cargo project to configure it for maximum performance, fast compile times or minimal binary size.

cargo hakari

cargo-hakari cargo-hakari-crates.io cargo-hakari-github cargo-hakari-lib.rs cat-development-tools::cargo-plugins

cargo-hakari⮳ manage "workspace-hack" packages to speed up builds in large workspaces.

cargo hakari is a command-line application to manage "workspace-hack" crates. Use it to speed up local cargo build and cargo check commands by up to 100x, and cumulatively by up to 1.7x or more.

Audit

Audit cargo.lock files for crates containing security vulnerabilities

cargo-audit cargo-audit-crates.io cargo-audit-github cargo-audit-lib.rs cat-development-tools cat-development-tools::cargo-plugins

cargo install cargo-audit
cargo audit

Embded the exact crate versions in your Rust executable for auditability

cargo-auditable cargo-auditable-crates.io cargo-auditable-github cargo-auditable-lib.rs cat-development-tools::cargo-plugins cat-encoding

cargo-auditable⮳ makes production Rust binaries auditable.

It audits binaries for known bugs or security vulnerabilities in production, at scale, with zero bookkeeping.

This works by embedding data about the dependency tree in JSON format into a dedicated linker section of the compiled executable.

List the license(s) of dependencies

cargo-license cargo-license-crates.io cargo-license-github cargo-license-lib.rs cat-development-tools::cargo-plugins

Cargo subcommand to see license of dependencies.

You can install cargo-license with cargo install cargo-license and run it in your project directory with: cargo license or cargo-license.

cargo deny

cargo-deny cargo-deny-crates.io cargo-deny-github cargo-deny-lib.rs cat-development-tools::cargo-plugins

cargo-deny is a cargo plugin that lets you lint your project's dependency graph to ensure all your dependencies conform to your expectations and requirements.

  • Checks the license information for each crate.
  • Checks for / bans specific crates in your graph, as well as duplicates.
  • Checks advisory databases for crates with security vulnerabilities, or that have been marked as Unmaintained, or which have been yanked from their source registry.
  • Checks the source location for each crate.

Install with:

cargo install --locked cargo-deny

# Or, if you're an Arch user
pacman -S cargo-deny
cargo deny init

cargo deny check
cargo deny check licenses

Maintain

Edit Cargo.toml

cargo-edit cargo-edit-crates.io cargo-edit-github cargo-edit-lib.rs cat-development-tools cat-development-tools::cargo-plugins

cargo edit provides commands for modifying a Cargo.toml file. It allows you to add, remove, and upgrade dependencies by modifying your Cargo.toml file from the command line.

Currently available subcommands:

  • cargo upgrade
  • cargo set-version

Find unused dependencies

cargo udeps

cargo-udeps cargo-udeps-crates.io cargo-udeps-github cargo-udeps-lib.rs cat-development-tools cat-development-tools::cargo-plugins

udeps⮳ find unused dependencies in Cargo.toml.

While compilation of this tool also works on Rust stable, it needs Rust nightly to actually run.

cargo machete

cargo-machete cargo-machete-crates.io cargo-machete-github cargo-machete-lib.rs

cargo-machete⮳ is a Cargo tool that detects unused dependencies in Rust projects, in a fast (yet imprecise) way.

Install and run with:

cargo install cargo-machete
cargo machete

Detect dependencies that are out of date

cargo-outdated cargo-outdated-crates.io cargo-outdated-github cargo-outdated-lib.rs

Cargo subcommand for displaying when dependencies are out of date.

If you are using VS Code, also look into the Dependi VS Code plugin.

Lint your crate API changes for semver violations

cargo-semver-checks cargo-semver-checks-crates.io cargo-semver-checks-github cargo-semver-checks-lib.rs cat-command-line-utilities cat-development-tools::cargo-plugins

cargo-semver-checks scans your Rust crate for semver violations.

# If you already use `cargo-binstall` for faster tool installations:
$ cargo binstall cargo-semver-checks

# Otherwise:
$ cargo install cargo-semver-checks --locked

# Lint a new release for SemVer breakage before `cargo publish`:
$ cargo semver-checks

Manage the cargo cache

cargo-cache cargo-cache-crates.io cargo-cache-github cargo-cache-lib.rs cat-development-tools cat-command-line-utilities cat-development-tools::cargo-plugins

cargo cache⮳ manages the cargo cache ($CARGO_HOME or ~/.cargo/), shows sizes and removes directories selectively.

Debugging, Logging

cat-development-tools::debugging

Help you figure out what is going on with your code such as logging, tracing, or assertions.

Tracing

Logging

Log Configuration

Alternatives

Diagnostic functions

Logs

tracing tracing-github cat-development-tools cat-development-tools::debugging

tracing_subscriber tracing_subscriber-crates.io

Add to Cargo.toml

[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"

Initialize the logger

Enable basic tracing

fn main() {
    // Filter events at runtime using the value
    // of the RUST_LOG environment variable:
    // for example, RUST_LOG=debug,my_crate=trace
    tracing_subscriber::fmt::init();

    // This is equivalent to:
    // tracing_subscriber::fmt()
    // .with_env_filter(EnvFilter::from_default_env())
    // .init();

    tracing::info!("tracing configured!");
    println!("Done.")
}

Combine layers

use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;

fn main() {
    tracing_subscriber::registry()
        .with(tracing_subscriber::fmt::layer())
        .with(tracing_subscriber::filter::EnvFilter::new(
            std::env::var("RUST_LOG").unwrap_or_else(|_| {
                "myproj=debug,axum=debug,tower_http=debug,mongodb=debug".into()
            }),
        ))
        .init();
    tracing::info!("tracing configured!");
    println!("Done.")
}

Or with a custom formatting layer

use tracing_subscriber::filter::EnvFilter;
use tracing_subscriber::fmt;
use tracing_subscriber::prelude::*;

fn main() {
    let fmt_layer = fmt::layer().with_target(false);
    let filter_layer = EnvFilter::try_from_default_env()
        .or_else(|_| EnvFilter::try_new("info"))
        .unwrap();

    tracing_subscriber::registry()
        .with(filter_layer)
        .with(fmt_layer)
        .init();
}

Configure a custom event formatter

use tracing_subscriber::fmt;

fn main() {
    // Configure a custom event formatter
    let format = fmt::format()
        .with_level(false) // Don't include levels in formatted output
        .with_target(false) // Don't include targets
        .with_thread_ids(true) // Include the thread ID of the current thread
        .with_thread_names(true) // Include the name of the current thread
        .compact(); // Use the `Compact` formatting style.

    // Create a `fmt` subscriber that uses our custom event format, and
    // set it as the default.
    tracing_subscriber::fmt().event_format(format).init();
}

Events

use tracing::Level;
use tracing::debug;
use tracing::error;
use tracing::event;
use tracing::info;
use tracing::trace;
use tracing::warn;

#[derive(Debug)]
struct S;

fn main() {
    event!(Level::INFO, "something happened");
    error!("error!");
    warn!("warning!");
    info!("info!");
    debug!("debug info!");
    trace!("trace info!");

    event!(target: "app_events", Level::TRACE, "something has happened!");

    // Records an event with two fields (also works for spans)
    event!(
        Level::INFO,
        answer = 42,
        question = "life, the universe, and everything"
    );

    // Unlike other fields, `message`'s shorthand initialization is just
    // the string itself.
    debug!(excitement = "yay!", "hello!");

    // Shorthand for `user = user`
    let user = "ferris";
    event!(Level::TRACE, "login: {}", user);

    // Note the `?`: `my_struct` will be recorded
    // using its `fmt::Debug` implementation.
    let my_struct = S;
    event!(Level::TRACE, greeting = ?my_struct);
}

Spans

use tracing::Level;
use tracing::span;

fn main() {
    let span = span!(Level::TRACE, "my_span");
    {
        // Current lexical scope.
        let _guard = span.enter();
        println!(
            "`enter` returns a RAII guard, which, when dropped, exits the span."
        );
        println!("Any trace events that occur here will occur within the span.")
    }
    println!("Dropping the guard exits the span.");
}

One-liner with .entered():

use tracing::Level;
use tracing::span;

fn main() {
    let span = span!(Level::TRACE, "some span").entered();

    println!("Code here is within the span");

    // optionally, explicitly exit the span, returning it
    let span = span.exit();

    println!("Code here is no longer within the span");

    // enter the span again
    let _span = span.entered();
    println!("Code here is within the span");
}

Holding the drop guard returned by Span::enter across .await points will result in incorrect traces. Use tracing::span::Span::in_scope⮳.

use tracing::Instrument;
use tracing::debug_span;
use tracing::info_span;

async fn my_async_function() {
    let span = info_span!("my_async_function");

    // Instrument synchronous code within an async functiom
    let _some_value = span.in_scope(|| {
        // Run some synchronous code inside the span...
        42
    });

    // This is okay!
    // The span has already been exited before we reach the await point.
    some_other_async_function().await;

    // Instrument async code
    async move {
        // This is correct! If we yield here, the span will be exited,
        // and re-entered when we resume.
        some_other_async_function().await;
    }
    .instrument(span) // instrument the async block with the span...
    .await; // ...and await it.

    let _some_value = some_other_async_function()
        .instrument(debug_span!("some_other_async_function"))
        .await;
}

async fn some_other_async_function() {}

#[tokio::main]
async fn main() {
    my_async_function().await;
}

Add tracing spans to functions

use tracing::Level;
use tracing::event;
use tracing::instrument;

#[instrument]
fn my_function(my_arg: usize) {
    // This event will be recorded inside a span named `my_function`
    // with the field `my_arg`.
    event!(Level::INFO, "inside my_function!");
    // ...
}

// Used on an async function
#[instrument(level = "info")]
async fn my_async_function() {
    // This is correct! If we yield here, the span will be exited,
    // and re-entered when we resume.
    some_other_async_function().await;
}

async fn some_other_async_function() {}

#[tokio::main]
async fn main() {
    my_function(42);
    my_async_function().await;
}

tracing_journald tracing_journald-crates.io tracing_journald-github tracing_journald-lib.rs

tracing_journald⮳ provides support for logging tracing⮳ events natively to journald⮳, preserving any structured information.

Log Messages

Log a debug message to the console

log env_logger cat-development-tools::debugging

The log⮳ crate provides logging utilities. The env_logger⮳ crate configures logging via an environment variable. The log::debug⮳ macro works like other std::fmt⮳ formatted strings.

fn execute_query(query: &str) {
    log::debug!("Executing query: {}", query);
}

fn main() {
    // env_logger is simple logger that can be configured via environment
    // variables. Example: RUST_LOG=info ./app
    env_logger::init();

    execute_query("DROP TABLE students");
}

No output prints when running this code. By default, the log level is error, and any lower levels are dropped.

Set the RUST_LOG⮳ environment variable to print the message:

RUST_LOG=debug cargo run

Cargo prints debugging information then the following line at the very end of the output:

DEBUG:main: Executing query: DROP TABLE students

Log an error message to the console

log env_logger cat-development-tools::debugging

Proper error handling considers exceptions exceptional. Here, an error logs to stderr with log's convenience macro log::error⮳.

fn execute_query(_query: &str) -> Result<(), &'static str> {
    Err("I'm afraid I can't do that")
}

fn main() {
    env_logger::init();

    let response = execute_query("DROP TABLE students");
    if let Err(err) = response {
        log::error!("Failed to execute query: {}", err);
    }
}

Log to stdout instead of stderr

log env_logger cat-development-tools::debugging

Creates a custom logger configuration using the env_logger::Builder::target⮳ to set the target of the log output to env_logger::fmt::Target

use env_logger::Target;

fn main() {
    env_logger::Builder::new().target(Target::Stdout).init();

    log::error!("This error has been printed to Stdout");
}

Log messages with a custom logger

log cat-development-tools::debugging

Implements a custom logger ConsoleLogger which prints to stdout. In order to use the logging macros, ConsoleLogger implements the log::Log⮳ trait and log::Log⮳ installs it.

use log::Level;
use log::LevelFilter;
use log::Metadata;
use log::Record;
use log::SetLoggerError;

static CONSOLE_LOGGER: ConsoleLogger = ConsoleLogger;

struct ConsoleLogger;

impl log::Log for ConsoleLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Info
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            println!("Rust says: {} - {}", record.level(), record.args());
        }
    }

    fn flush(&self) {}
}

fn main() -> Result<(), SetLoggerError> {
    log::set_logger(&CONSOLE_LOGGER)?;
    log::set_max_level(LevelFilter::Info);

    log::info!("hello log");
    log::warn!("warning");
    log::error!("oops");
    Ok(())
}

Log to the Unix syslog

log syslog cat-development-tools::debugging

Logs messages to UNIX syslog⮳. Initializes logger backend with syslog::initsyslog::init⮳ records the program submitting the log entry's classification, syslog::init⮳ denotes allowed log verbosity and Option<&str> holds optional application name.


#[cfg(target_os = "linux")]
fn main() -> anyhow::Result<()> {
    use syslog::Facility;

    syslog::init(
        Facility::LOG_USER,
        log::LevelFilter::Debug,
        Some("My app name"),
    )?;
    log::debug!("this is a debug {}", "message");
    log::error!("this is an error!");
    Ok(())
}

#[cfg(not(target_os = "linux"))]
fn main() -> anyhow::Result<()> {
    println!("So far, only Linux systems are supported.");
    Ok(())
}

Configure Logging

Enable log levels per module

log env_logger cat-development-tools::debugging

Creates two modules foo and nested foo::bar with logging directives controlled separately with RUST_LOG⮳ environmental variable.

mod foo {
    mod bar {
        pub fn run() {
            log::warn!("[bar] warn");
            log::info!("[bar] info");
            log::debug!("[bar] debug");
        }
    }

    pub fn run() {
        log::warn!("[foo] warn");
        log::info!("[foo] info");
        log::debug!("[foo] debug");
        bar::run();
    }
}

fn main() {
    env_logger::init();
    log::warn!("[root] warn");
    log::info!("[root] info");
    log::debug!("[root] debug");
    foo::run();
}

The RUST_LOG environment variable controls env-logger⮳ output. Module declarations take comma separated entries formatted like path::to::module=log_level. Run the test application as follows:

RUST_LOG="warn,test::foo=info,test::foo::bar=debug" test

Sets the default log::Level⮳ to warn, module foo and module foo::bar to info and debug.

WARN:test: [root] warn
WARN:test::foo: [foo] warn
INFO:test::foo: [foo] info
WARN:test::foo::bar: [bar] warn
INFO:test::foo::bar: [bar] info
DEBUG:test::foo::bar: [bar] debug

Use a custom environment variable to set up logging

log env_logger cat-development-tools::debugging

env_logger::Builder⮳ configures logging.

env_logger::Builder::parse⮳ parses MY_APP_LOG environment variable contents in the form of RUST_LOG⮳ syntax. Then, env_logger::Builder::init⮳ initializes the logger. All these steps are normally done internally by env_logger::init⮳.


fn main() {
    init_logger();

    log::info!("informational message");
    log::warn!("warning message");
    log::error!("this is an error {}", "message");
    if log::log_enabled!(log::Level::Info) {
        let x = 3 * 4; // "Expensive" computation
        log::trace!("the answer was: {}", x);
    }
}

//#[cfg(not(test))]
fn init_logger() {
    // env_logger is a simple logger that can be configured via environment
    // variables. Example: RUST_LOG=info ./app
    // Typically you would use:
    // env_logger::init();

    // Initialise a logger with filter level Off,
    // then override the log filter from an environment variable called
    // MY_APP_LOG:
    env_logger::Builder::new()
        .filter_level(log::LevelFilter::Off)
        .parse_env("MY_APP_LOG")
        .init();

    // Alternatively, `Env` lets us tweak what the environment
    // variables to read are and what the default
    // value is if they're missing
    // let env = env_logger::Env::default()
    // // Specify an environment variable to read the filter from.
    // // If the variable is not set, the default value will be used.
    // .filter_or("MY_APP_LOG", "trace")
    // .write_style_or("MY_APP_LOG_STYLE", "always");
    // env_logger::init_from_env(env);
}

Include a timestamp in log messages

log env_logger chrono cat-development-tools::debugging

Creates a custom logger configuration with env_logger::Builder⮳ Each log entry calls chrono::offset::Local::now⮳ to get the current chrono::DateTime⮳ in local timezone and uses chrono::DateTime::format⮳ with chrono::format::strftime⮳ to format a timestamp used in the final log.

The example calls env_logger::Builder::format⮳ to set a closure which formats each message text with timestamp, log::Record::level⮳ and body (log::Record::args⮳).

use std::io::Write;

use chrono::Local;
use env_logger::Builder;
use log::LevelFilter;

fn main() {
    Builder::new()
        .format(|buf, record| {
            writeln!(
                buf,
                "{} [{}] - {}",
                Local::now().format("%Y-%m-%dT%H:%M:%S"),
                record.level(),
                record.args()
            )
        })
        .filter(None, LevelFilter::Info)
        .init();

    log::warn!("warn");
    log::info!("info");
    log::debug!("debug");
}

stderr output will contain

2017-05-22T21:57:06 [WARN] - warn
2017-05-22T21:57:06 [INFO] - info

Log messages to a custom location

log log4rs cat-development-tools::debugging

log4rs⮳ configures log output to a custom location. log4rs⮳ can use either an external YAML file or a builder configuration.

Create the log configuration with log4rs::append::file::FileAppender⮳ An appender defines the logging destination. The configuration continues with encoding using a custom pattern from log4rs::encode::pattern⮳ Assigns the configuration to log4rs::config::Config⮳ and sets the default log::LevelFilter

use anyhow::Result;
use log::LevelFilter;
use log4rs::append::file::FileAppender;
use log4rs::config::Appender;
use log4rs::config::Config;
use log4rs::config::Root;
use log4rs::encode::pattern::PatternEncoder;

fn main() -> Result<()> {
    let logfile = FileAppender::builder()
        .encoder(Box::new(PatternEncoder::new("{l} - {m}\n")))
        .build("temp/log/output.log")?;

    let config = Config::builder()
        .appender(Appender::builder().build("logfile", Box::new(logfile)))
        .build(Root::builder().appender("logfile").build(LevelFilter::Info))?;

    log4rs::init_config(config)?;

    log::info!("Hello, world!");

    Ok(())
}

Alternatives

Use older alternatives to tracing

tracing is now the "go-to" crate for logging, but log, slog and log4rs are still in extensive use.

log

log log-crates.io log-github log-lib.rs cat-development-tools::debugging

log is an older and simpler crate, if your needs are simple and you are not using any async code.

slog

slog slog-crates.io slog-github slog-lib.rs cat-development-tools::debugging

Structured, extensible, composable logging. slog remains a stable, featureful and battle-tested library, used in many important projects.

fn main() {
    todo!();
}

log4rs

log4rs log4rs-crates.io log4rs-github log4rs-lib.rs

log4rs is a highly configurable multi-output logging implementation for the log facade.

use log::{error, info, warn};
use log4rs;

fn main() {
    log4rs::init_file("config/log4rs.yaml", Default::default()).unwrap();

    info!("booting up");

    // ...
}

env_logger

env_logger env_logger-crates.io env_logger-github env_logger-lib.rs cat-development-tools::debugging

A logging implementation for log which is configured via an environment variable. env_logger makes sense when used in executables (binary projects). Libraries should use the log crate instead.

use log::info;

fn main() {
    env_logger::init();

    info!("starting up");

    // ...
}

Other frameworks

OpenTelemetry

OpenTelemetry Rust documentation

fn main() {
    todo!();
}

OpenObserve

OpenObserve⮳ (written in Rust) is a petabyte-scale Elasticsearch/Splunk/Datadog alternative for logs, metrics, traces, RUM, error tracking, and session replay.

fn main() {
    todo!();
}

Diagnostic functions

Get the type name of the pointed-to value

std

fn get_iter() -> impl Iterator<Item = i32> {
    [1, 2, 3].into_iter()
}

fn main() {
    let iter = get_iter();
    // NEW in Rust 1.76
    // Returns the type name of the pointed-to value as a string slice.
    let iter_name = std::any::type_name_of_val(&iter);
    let sum: i32 = iter.sum();
    println!("The sum of the `{iter_name}` is {sum}.");
    // prints: The sum of the `core::array::iter::IntoIter<i32, 3>` is 6.
}

Encoding

cat-encoding

Encoding and/or decoding data from one data format to another.

Character Sets

CSV Processing

Structured Data

Serde

RecipeCratesCategories
Serialize JSONserde_jsoncat-encoding
monostatemonostatecat-encoding
serde-ignoredserde-ignoredcat-encoding

Character Sets

Percent-encode a string

percent_encoding cat-encoding

Encode an input string with percent_encoding⮳ using the percent_encoding::utf8_percent_encode⮳ function from the percent_encoding crate. Then decode using the percent_encoding::percent_decode⮳ function.

use std::str::Utf8Error;

use percent_encoding::AsciiSet;
use percent_encoding::CONTROLS;
use percent_encoding::percent_decode;
use percent_encoding::utf8_percent_encode;

/// https://url.spec.whatwg.org/#fragment-percent-encode-set
const FRAGMENT: &AsciiSet =
    &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');

fn main() -> Result<(), Utf8Error> {
    let input = "confident, productive systems programming";

    let iter = utf8_percent_encode(input, FRAGMENT);
    let encoded: String = iter.collect();
    println!("{}", encoded);
    assert_eq!(encoded, "confident,%20productive%20systems%20programming");

    let iter = percent_decode(encoded.as_bytes());
    let decoded = iter.decode_utf8()?;
    println!("{}", decoded);
    assert_eq!(decoded, "confident, productive systems programming");

    Ok(())
}

The encode set defines which bytes (in addition to non-ASCII and controls) need to be percent-encoded. The choice of this set depends on context. For example, url encodes ? in a URL path but not in a query string.

The return value of encoding is an iterator of &str slices which collect into a std::string::String⮳.

Encode a string as application/x-www-form-urlencoded

url url-crates.io url-github url-lib.rscat-encodingcat-no-stdcat-parser-implementationscat-web-programming

Encodes a string into application/x-www-form-urlencoded syntax using the form_urlencoded::byte_serialize⮳ and subsequently decodes it with form_urlencoded::parse⮳. Both functions return iterators that collect into a std::string::String⮳.

use url::form_urlencoded::byte_serialize;
use url::form_urlencoded::parse;

fn main() {
    let urlencoded: String = byte_serialize("What is ❤?".as_bytes()).collect();
    assert_eq!(urlencoded, "What+is+%E2%9D%A4%3F");
    println!("urlencoded:'{}'", urlencoded);

    let decoded: String = parse(urlencoded.as_bytes())
        .map(|(key, val)| [key, val].concat())
        .collect();
    assert_eq!(decoded, "What is ❤?");
    println!("decoded:'{}'", decoded);
}

Encode and decode hexadecimal

data-encoding cat-encoding

The data_encoding⮳ crate provides a HEXUPPER::encode method which takes a &[u8] and returns a std::string::String⮳ containing the hexadecimal representation of the data.

Similarly, a HEXUPPER::decode method is provided which takes a &[u8] and returns a Vec<u8> if the input data is successfully decoded.

The example below coverts &[u8] data to hexadecimal equivalent. Compares this value to the expected value.

use data_encoding::DecodeError;
use data_encoding::HEXUPPER;

fn main() -> Result<(), DecodeError> {
    let original = b"The quick brown fox jumps over the lazy dog.";
    let expected = "54686520717569636B2062726F776E20666F78206A756D7073206F76\
        657220746865206C617A7920646F672E";

    let encoded = HEXUPPER.encode(original);
    println!("{}", encoded);
    assert_eq!(encoded, expected);

    let decoded = HEXUPPER.decode(&encoded.into_bytes())?;
    println!("{:?}", decoded);
    assert_eq!(&decoded[..], &original[..]);

    Ok(())
}

Encode and decode base64

base64 cat-encoding

Encodes byte slice into base64 String using base64::encode and decodes it with base64::decode.

use std::str;

use anyhow::Result;
use base64::prelude::*;

fn main() -> Result<()> {
    let hello = b"hello rustaceans";
    let encoded: String = BASE64_STANDARD.encode(hello);
    let decoded: Vec<u8> = BASE64_STANDARD.decode(&encoded)?;

    println!("origin: {}", str::from_utf8(hello)?);
    println!("base64 encoded: {}", encoded);
    println!("back to origin: {}", str::from_utf8(&decoded)?);

    Ok(())
}

CSV processing

Read CSV records

csv cat-encoding

Reads standard CSV records into csv::StringRecord⮳ — a weakly typed data representation which expects valid UTF-8 rows. Alternatively, csv::ByteRecord⮳ makes no assumptions about UTF-8.

use csv::Error;

fn main() -> Result<(), Error> {
    let csv = "year,make,model,description
  1948,Porsche,356,Luxury sports car
  1967,Ford,Mustang fastback 1967,American car";

    let mut reader = csv::Reader::from_reader(csv.as_bytes());
    for record in reader.records() {
        let record = record?;
        println!(
            "In {}, {} built the {} model. It is a {}.",
            &record[0], &record[1], &record[2], &record[3]
        );
    }

    Ok(())
}

serde⮳ deserializes data into strongly type structures. See the csv::Reader::deserialize⮳ method.

use serde::Deserialize;
#[derive(Deserialize)]
struct Record {
    year: u16,
    make: String,
    model: String,
    description: String,
}

fn main() -> Result<(), csv::Error> {
    let csv = "year,make,model,description
1948,Porsche,356,Luxury sports car
1967,Ford,Mustang fastback 1967,American car";

    let mut reader = csv::Reader::from_reader(csv.as_bytes());

    for record in reader.deserialize() {
        let record: Record = record?;
        println!(
            "In {}, {} built the {} model. It is a {}.",
            record.year, record.make, record.model, record.description
        );
    }

    Ok(())
}

Read CSV records with different delimiter

csv cat-encoding

Reads CSV records with a tab csv::ReaderBuilder::delimiter⮳.

use csv::Error;
use csv::ReaderBuilder;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Record {
    name: String,
    place: String,
    #[serde(deserialize_with = "csv::invalid_option")]
    id: Option<u64>,
}

fn main() -> Result<(), Error> {
    let data = "name\tplace\tid
  Mark\tMelbourne\t46
  Ashley\tZurich\t92";

    let mut reader = ReaderBuilder::new()
        .delimiter(b'\t')
        .from_reader(data.as_bytes());
    for result in reader.deserialize::<Record>() {
        println!("{:?}", result?);
    }

    Ok(())
}

Filter CSV records matching a predicate

csv cat-encoding

Returns only the rows from data with a field that matches query.

use std::io;

use anyhow::Result;

fn main() -> Result<()> {
    let query = "CA";
    let data = "\
City,State,Population,Latitude,Longitude
Kenai,AK,7610,60.5544444,-151.2583333
Oakman,AL,,33.7133333,-87.3886111
Sandfort,AL,,32.3380556,-85.2233333
West Hollywood,CA,37031,34.0900000,-118.3608333";

    let mut rdr = csv::ReaderBuilder::new().from_reader(data.as_bytes());
    let mut wtr = csv::Writer::from_writer(io::stdout());

    wtr.write_record(rdr.headers()?)?;

    for result in rdr.records() {
        let record = result?;
        if record.iter().any(|field| field == query) {
            wtr.write_record(&record)?;
        }
    }

    wtr.flush()?;
    Ok(())
}

This example has been adapted from the csv crate tutorial

Handle invalid CSV data with serde

csv serde cat-encoding

CSV files often contain invalid data. For these cases, the csv⮳ crate provides a custom deserializer, csv::invalid_option⮳ which automatically converts invalid data to std::option::Option::None⮳ values.

use csv::Error;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Record {
    name: String,
    place: String,
    #[serde(deserialize_with = "csv::invalid_option")]
    id: Option<u64>,
}

fn main() -> Result<(), Error> {
    let data = "name,place,id
mark,sydney,46.5
ashley,zurich,92
akshat,delhi,37
alisha,colombo,xyz";

    let mut rdr = csv::Reader::from_reader(data.as_bytes());
    for result in rdr.deserialize() {
        let record: Record = result?;
        println!("{:?}", record);
    }

    Ok(())
}

Serialize records to CSV

csv cat-encoding

This example shows how to serialize a Rust tuple. csv::writer⮳ supports automatic serialization from Rust types into CSV records. csv::Writer::write_record⮳ writes a simple record containing string data only. Data with more complex values such as numbers, floats, and options use csv::Writer::serialize⮳. Since CSV writer uses an internal buffer, always explicitly csv::Writer::flush⮳ when done.

use std::io;

use anyhow::Result;

fn main() -> Result<()> {
    let mut wtr = csv::Writer::from_writer(io::stdout());

    wtr.write_record(["Name", "Place", "ID"])?;

    wtr.serialize(("Mark", "Sydney", 87))?;
    wtr.serialize(("Ashley", "Dublin", 32))?;
    wtr.serialize(("Akshat", "Delhi", 11))?;

    wtr.flush()?;
    Ok(())
}

Serialize records to CSV using serde

csv serde cat-encoding

The following example shows how to serialize custom structs as CSV records using the serde⮳ crate.

use std::io;

use anyhow::Result;
use serde::Serialize;

#[derive(Serialize)]
struct Record<'a> {
    name: &'a str,
    place: &'a str,
    id: u64,
}

fn main() -> Result<()> {
    let mut wtr = csv::Writer::from_writer(io::stdout());

    let rec1 = Record {
        name: "Mark",
        place: "Melbourne",
        id: 56,
    };
    let rec2 = Record {
        name: "Ashley",
        place: "Sydney",
        id: 64,
    };
    let rec3 = Record {
        name: "Akshat",
        place: "Delhi",
        id: 98,
    };

    wtr.serialize(rec1)?;
    wtr.serialize(rec2)?;
    wtr.serialize(rec3)?;

    wtr.flush()?;

    Ok(())
}

Transform a CSV column

csv serde cat-encoding

Transform a CSV file containing a color name and a hex color into one with a color name and an rgb color. Utilizes the csv⮳ crate to read and write the csv file, and serde⮳ to deserialize and serialize the rows to and from bytes.

See csv::Reader::deserialize⮳, serde::Deserialize⮳ and std::str::FromStr⮳.

use std::str::FromStr;

use anyhow::Result;
use anyhow::anyhow;
use serde::Deserialize;
use serde::Deserializer;
use serde::de;

#[derive(Debug, Deserialize)]
struct Row {
    color_name: String,
    color: HexColor,
}

#[derive(Debug)]
struct HexColor {
    red: u8,
    green: u8,
    blue: u8,
}

impl FromStr for HexColor {
    type Err = anyhow::Error;

    fn from_str(hex_color: &str) -> std::result::Result<Self, Self::Err> {
        let trimmed = hex_color.trim_matches('#');
        if trimmed.len() != 6 {
            Err(anyhow!("Invalid length of hex string"))
        } else {
            Ok(HexColor {
                red: u8::from_str_radix(&trimmed[..2], 16)?,
                green: u8::from_str_radix(&trimmed[2..4], 16)?,
                blue: u8::from_str_radix(&trimmed[4..6], 16)?,
            })
        }
    }
}

impl<'de> Deserialize<'de> for HexColor {
    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        FromStr::from_str(&s).map_err(de::Error::custom)
    }
}

fn main() -> Result<()> {
    let data = "color_name,color
red,#ff0000
green,#00ff00
blue,#0000FF
periwinkle,#ccccff
magenta,#ff00ff"
        .to_owned();
    let mut out = csv::Writer::from_writer(vec![]);
    let mut reader = csv::Reader::from_reader(data.as_bytes());
    // Deserialize as Row, using the implementation above
    for result in reader.deserialize() {
        // We need to provide a type hint for automatic deserialization.
        let res: Row = result?;

        // Serialize the tuple as CSV into Vec<u8>
        out.serialize((
            res.color_name,
            res.color.red,
            res.color.green,
            res.color.blue,
        ))?;
    }
    let written = String::from_utf8(out.into_inner()?)?;
    assert_eq!(Some("magenta,255,0,255"), written.lines().last());
    println!("{}", written);
    Ok(())
}

Structured Data

Serialize and deserialize unstructured JSON

serde_json cat-encoding

The serde_json⮳ crate provides a serde_json::from_str⮳ function to parse a &str of JSON.

Unstructured JSON can be parsed into a universal serde_json::Value⮳ type that is able to represent any valid JSON data.

The example below shows a &str of JSON being parsed. The expected value is declared using the serde_json::json⮳ macro.

use serde_json::Error;
use serde_json::Value;
use serde_json::json;

fn main() -> Result<(), Error> {
    let j: &str = r#"{
                 "userid": 103609,
                 "verified": true,
                 "access_privileges": [
                   "user",
                   "admin"
                 ]
               }"#;

    let parsed: Value = serde_json::from_str(j)?;

    let expected: Value = json!({
        "userid": 103609,
        "verified": true,
        "access_privileges": [
            "user",
            "admin"
        ]
    });
    println!("{}", expected);
    assert_eq!(parsed, expected);

    Ok(())
}

Deserialize a TOML configuration file

toml cat-encoding

TOML is a simple, ergonomic, and readable configuration format that is often used by Rust's tooling - for example cargo. The following parses some TOML into a universal toml::Value that is able to represent any valid TOML data.

use toml::Value;
use toml::de::Error;

fn main() -> Result<(), Error> {
    // Note the use of a raw string,
    // so that there is no need to escape the inner double quotes
    let toml_content = r#"
          [package]
          name = "your_package"
          version = "0.1.0"
          authors = ["You! <you@example.org>"]

          [dependencies]
          serde = "1.0"
          "#;

    let package_info: Value = toml::from_str(toml_content)?;

    assert_eq!(package_info["dependencies"]["serde"].as_str(), Some("1.0"));
    println!(
        "Package name: {}",
        package_info["package"]["name"].as_str().unwrap()
    );
    Ok(())
}

Parse TOML into your own structs using serde⮳.

use std::collections::HashMap;

use serde::Deserialize;
use toml::de::Error;

#[derive(Deserialize, Debug)]
struct Config {
    package: Package,
    dependencies: HashMap<String, String>,
}

#[derive(Deserialize, Debug)]
struct Package {
    name: String,
    version: String,
    authors: Vec<String>,
}

fn main() -> Result<(), Error> {
    let toml_content = r#"
          [package]
          name = "your_package"
          version = "0.1.0"
          authors = ["You! <you@example.org>"]

          [dependencies]
          serde = "1.0"
          "#;

    let package_info: Config = toml::from_str(toml_content)?;
    println!("{:?}", package_info);

    assert_eq!(package_info.package.name, "your_package");
    assert_eq!(package_info.package.version, "0.1.0");
    assert_eq!(package_info.package.authors, vec!["You! <you@example.org>"]);
    assert_eq!(package_info.dependencies["serde"], "1.0");

    Ok(())
}

Read and write integers in little-endian byte order

byteorder byteorder-crates.io byteorder-github byteorder-lib.rs cat-encoding cat-no-std cat-parsing

byteorder⮳ is a library for reading/writing numbers in big-endian and little-endian. It can reverse the significant bytes of structured data. This may be necessary when receiving information over the network, when bytes received are from another system.

use std::io::Error;

use byteorder::LittleEndian;
use byteorder::ReadBytesExt;
use byteorder::WriteBytesExt;

#[derive(Default, PartialEq, Debug)]
struct Payload {
    kind: u8,
    value: u16,
}

fn encode(payload: &Payload) -> Result<Vec<u8>, Error> {
    let mut bytes = vec![];
    bytes.write_u8(payload.kind)?;
    bytes.write_u16::<LittleEndian>(payload.value)?;
    Ok(bytes)
}

fn decode(mut bytes: &[u8]) -> Result<Payload, Error> {
    let payload = Payload {
        kind: bytes.read_u8()?,
        value: bytes.read_u16::<LittleEndian>()?,
    };
    Ok(payload)
}

fn main() -> Result<(), Error> {
    let original_payload = Payload::default();
    let encoded_bytes = encode(&original_payload)?;
    println!("{:?}", encoded_bytes);
    let decoded_payload = decode(&encoded_bytes)?;
    assert_eq!(original_payload, decoded_payload);
    println!("{:?}", decoded_payload);
    Ok(())
}

Serialization

RecipeCratesCategories
Serialize JSONserde_jsoncat-encoding
monostatemonostatecat-encoding
serde-ignoredserde-ignoredcat-encoding

serde serde-crates.io serde-github serde-lib.rs

De facto standard serialization library. Use in conjunction with sub-crates like serde_json for the specific format that you are using.

fn main() {
    todo!();
}

Serialize JSON

serde_json serde_json-crates.io serde_json-github serde_json-lib.rs

fn main() {
    todo!();
}

See also

monostate

monostate monostate-crates.io monostate-github monostate-lib.rs

This library implements a type macro for a zero-sized type that is Serde deserializable only from one specific value.

fn main() {
    todo!();
}

serde-ignored

serde-ignored serde-ignored-crates.io serde-ignored-github serde-ignored-lib.rs

fn main() {
    todo!();
}

Typecasts

RecipeCratesCategories
bytemuckbytemuckcat-encoding
zerocopyzerocopycat-encoding

bytemuck

bytemuck bytemuck-crates.io bytemuck-github bytemuck-lib.rs cat-encoding cat-no-std cat-encoding

fn main() {
    todo!();
}

zerocopy

zerocopy zerocopy-crates.io zerocopy-github zerocopy-lib.rs cat-encoding cat-parsing cat-rust-patterns cat-embedded cat-no-std::no-alloc cat-encoding

fn main() {
    todo!();
}

Binary encoders

bincode

bincode bincode-crates.io bincode-github bincode-lib.rs cat-encoding cat-network-programming

A binary serialization / deserialization strategy for transforming structs into bytes and vice versa.

ProtoBuf

prost

prost prost-crates.io prost-github prost-lib.rs cat-encoding

prost is a Protocol Buffers implementation for the Rust Language.

protobuf

protobuf protobuf-crates.io protobuf-github protobuf-lib.rs

protobuf is a Rust implementation of Google protocol buffers.

MessagePack with rmp-serde

rmp-serde rmp-serde-crates.io rmp-serde-github rmp-serde-lib.rs cat-encoding

This crate connects Rust MessagePack library with serde providing an ability to easily serialize and deserialize both Rust built-in types, the standard library and custom data structures.

CBOR with ciborium

ciborium ciborium-crates.io ciborium-github ciborium-lib.rs cat-data-structures cat-no-std cat-embedded cat-encoding cat-parsing

Concise Binary Object Representation is a binary data serialization format loosely based on JSON. ciborium is a serde implementation of CBOR using ciborium-basic.

Error Handling

Trigger and handle irrecoverable panics

std cat-rust-patterns

The panic!(...) macro allows a program to terminate immediately and provide feedback to the caller of the program.

fn main() {
    panic!("Crash and burn");
}

panic! is closely tied with the unwrap method of both Option and Result enums. Both implementations call panic! when they are set to None or Err variants.

// use std::str::FromStr;

fn main() {
    let number_str = "42";

    // `parse()` attempts to convert the string into a number.
    // This operation can fail if the string isn't a valid number, so it returns
    // a `Result<u32, ParseIntError>`.
    let number: u32 = number_str.parse().unwrap();
    // `unwrap()` is called on the `Result` to extract the `u32` value.
    // - If the parsing is successful, the value is assigned to `number`.
    // - If the parsing fails, the program panics with an error message.

    println!("The number is: {}", number);
}

Provide a fallback value with unwrap_or_else

std cat-rust-patterns

use std::fs;
use std::fs::File;
use std::io::ErrorKind;

fn main() {
    if !fs::exists("temp").unwrap() {
        fs::create_dir("temp").unwrap();
    }
    let _greeting_file = File::open("temp/hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("temp/hello.txt").unwrap_or_else(|error| {
                panic!("Problem creating the file: {:?}", error);
            })
        } else {
            panic!("Problem opening the file: {:?}", error);
        }
    });
}

Return recoverable errors with Result

std cat-rust-patterns

use std::io;
use std::io::BufRead;

fn main() {
    let mut cursor = io::Cursor::new(b"foo\nbar");
    let mut buf = String::new();

    cursor
        // `read_line` puts whatever the user enters into the string we pass to it,
        // but it also returns a `Result` value.
        .read_line(&mut buf)
        // If this instance of `Result` is an `Err` value, expect will cause the program to crash
        // and display the message that you passed as an argument to expect.
        .expect("Failed to read line");

    // Alternative: `unwrap` panics if there is an error
    // let _greeting_file = std::fs::File::open("temp/hello.txt").unwrap();
}

Propagate errors with the ? operator

std cat-rust-patterns

use std::fs::File;
use std::io;
use std::io::Read;

fn read_username_from_file() -> Result<String, io::Error> {
    let mut username_file = File::open("temp/hello.txt")?;
    let mut username = String::new();
    username_file.read_to_string(&mut username)?;
    Ok(username)
}

fn main() {
    if !std::fs::exists("temp").unwrap() {
        std::fs::create_dir("temp").unwrap();
    }
    match read_username_from_file() {
        Ok(name) => println!("User name: {}", name),
        Err(err) => println!("Error: {}", err),
    }
}

If the value of the Result is an Ok, the value inside the Ok will get returned from this expression, and the program will continue. If the value is an Err, the Err will be returned from the whole function, as if we had used the return keyword, so the error value gets propagated to the calling code.

This error points out that we’re only allowed to use the ? operator in a function that returns Result, Option, or another type that implements std::ops::FromResidual⮳.

Another example:

use std::error::Error;

fn parse_port(s: &str) -> Result<u16, Box<dyn Error>> {
    // We need to use `Box<dyn Error>`, because the returned error type
    // cannot be determined during compile time: It will either
    // contain an instance of `std::num::ParseIntError` (from the parse
    // method, when parsing fails), or a string (when the port is
    // zero). Alternatively, you may use `anyhow::Result`.
    let port: u16 = s.parse()?;
    if port == 0 {
        Err(Box::from(format!("Invalid port: {}", port)))
    } else {
        Ok(port)
    }
}

fn main() {
    match parse_port("123") {
        Ok(port) => println!("Port: {}", port),
        Err(err) => panic!("{}", err),
    }
}

std::io defines the type alias type Result<T> = std::result::Result<T, std::io::Error>;

Handle errors correctly in main

cat-rust-patterns.

std::io::Error⮳ defined type implementing the std::error::Error⮳ trait.

The below recipe will tell how long the system has been running by opening the Unix file /proc/uptime and parse the content to get the first number. It returns the uptime, unless there is an error.

use std::fs::File;
use std::io::Read;

use anyhow::Result;
use anyhow::anyhow;

fn read_uptime() -> Result<u64> {
    let mut uptime = String::new();
    File::open("/proc/uptime")?.read_to_string(&mut uptime)?;

    Ok(uptime
        .split('.')
        .next()
        .ok_or(anyhow!("Cannot parse uptime data"))?
        .parse()?)
}

fn main() {
    match read_uptime() {
        Ok(uptime) => println!("uptime: {} seconds", uptime),
        Err(err) => eprintln!("error: {}", err),
    };
}

Avoid discarding errors during error conversions

reqwest reqwest-crates.io reqwest-github reqwest-lib.rs cat-wasm cat-web-programming::http-client cat-rust-patterns

Uses reqwest⮳::blocking⮳ to query a random integer generator web service. Converts the string response into an integer.


fn parse_response(
    response: reqwest::blocking::Response,
) -> anyhow::Result<u32> {
    let body = response.text()?;
    let body = body.trim();
    // println!("Body: {body}");
    let b = body.parse::<u32>()?;
    Ok(b)
}

fn main() -> anyhow::Result<()> {
    let url = "https://www.random.org/integers/?num=1&min=0&max=10&col=1&base=10&format=plain".to_string();
    let response = reqwest::blocking::get(url)?;
    let random_value: u32 = parse_response(response)?;
    println!("A random number between 0 and 10: {}", random_value);
    Ok(())
}

Obtain the backtrace in complex error scenarios

cat-rust-patterns

This recipe shows how to handle a complex error scenario and then print a backtrace. It relies on to extend errors by appending new errors.

The below recipes attempts to deserialize the value 256 into a u8⮳. An error will bubble up from Serde then csv and finally up to the user code.


// use std::fmt;

// use anyhow::anyhow;
// use anyhow::Context;
use anyhow::Result;
// use serde::Deserialize;

// #[derive(Debug, Deserialize)]
// struct Rgb {
//     red: u8,
//     blue: u8,
//     green: u8,
// }

// impl Rgb {
//     fn from_reader(csv_data: &[u8]) -> Result<Rgb> {
//         let c = csv::Reader::from_reader(csv_data)
//             .deserialize()
//             .nth(0)
//             .ok_or(anyhow!(""))?;
//         let color = c.context("")?;

//         Ok(color)
//     }
// }

// impl fmt::UpperHex for Rgb {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         let hexa = u32::from(self.red) << 16
//             | u32::from(self.blue) << 8
//             | u32::from(self.green);
//         write!(f, "{:X}", hexa)
//     }
// }

// fn main() -> Result<()> {
//     let csv = "red,blue,green
// 102,256,204";

//     let rgb = Rgb::from_reader(csv.as_bytes())?;
//     println!("{:?} to hexadecimal #{:X}", rgb, rgb);

//     Ok(())
// }

fn main() -> Result<()> {
    Ok(())
}

Backtrace error rendered:

Error level - description
└> 0 - Cannot read CSV data
└> 1 - Cannot deserialize RGB color
└> 2 - CSV deserialize error: record 1 (line: 2, byte: 15): field 1: number too large to fit in target type
└> 3 - field 1: number too large to fit in target type

Run the recipe with RUST_BACKTRACE=1 to display a detailed backtrace associated with this error.

Custom Errors

RecipeCratesCategories
anyhowanyhowcat-rust-patterns
thisErrorthiserrorcat-rust-patterns
miettemiettecat-rust-patterns
color-eyrecolor-eyrecat-rust-patterns

Use anyhow⮳ if you don't care what error type your functions return, you just want it to be easy. This is common in application code. Use thiserror⮳ if you are a library that wants to design your own dedicated error type(s) so that on failures the caller gets exactly the information that you choose.

anyhow

anyhow anyhow-crates.io anyhow-github anyhow-lib.rs cat-no-std cat-rust-patterns

anyhow provides a flexible concrete Error type built on std::error::Error.

Use Result<T, anyhow::Error> or equivalently anyhow::Result<a name="a008"></a><T>⮳ as the return type of any fallible function.

use anyhow::Context;
use anyhow::Result;

fn do_something() -> Result<()> {
    Err(anyhow::Error::msg("Some Error"))
}

fn main() -> anyhow::Result<()> {
    // ...
    do_something().context("Failed to do the important thing")?; // Provide context to the error

    let _content = std::fs::read("/notafile.txt")
        .with_context(|| "Failed to read instrs from file".to_string())?;

    Ok(())
}

Anyhow works with any error type that has an impl of std::error::Error, including ones defined in your crate e.g. using thiserror⮳.

thisError

thiserror thiserror-crates.io thiserror-github thiserror-lib.rs cat-rust-patterns

thiserror⮳ provides a convenient derive⮳ macro for the standard library’s std::error::Error trait.


use thiserror::Error;

#[derive(Error, Debug)]
pub enum DataStoreError {
    // A Display impl is generated for your error if you provide
    // #[error("...")] messages on the struct or each variant of your enum
    #[error("data store disconnected")]
    Disconnect(#[from] std::io::Error), /* A From impl is generated for
                                         * each variant containing
                                         * a #[from] attribute. */

    #[error("the data for key `{0}` is not available")]
    Redaction(String),

    #[error("invalid header (expected {expected:?}, found {found:?})")]
    InvalidHeader { expected: String, found: String },

    #[error("unknown data store error")]
    Unknown,

    #[error(transparent)]
    // Forward the source and Display methods straight through to an
    // underlying error without adding an additional message.
    Other(#[from] anyhow::Error),
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Return an error:
    Err(DataStoreError::Unknown)?;
    Ok(())
}

The #[error(...)] messages support a shorthand for interpolating fields from the error.

#[error("{var}")]  //⟶ write!("{}", self.var)
#[error("{0}")]   //⟶ write!("{}", self.0)
#[error("{var:?}")] //⟶ write!("{:?}", self.var)
#[error("{0:?}")]  //⟶ write!("{:?}", self.0)
use thiserror::Error;

#[derive(Error, Debug)]
pub struct MyError {
    msg: String,

    // The Error trait’s source() method is implemented to return whichever
    // field has a #[source] attribute or is named source, if any. This is
    // for identifying the underlying lower level error that caused your
    // error. #[from] implies #[source]. Any error type that implements
    // `std::error::Error` or dereferences to `dyn std::error::Error` will work
    // as a source.
    #[source]
    source: std::io::Error,
    // Automatically detected to implement provide()
    // backtrace: std::backtrace::Backtrace,
}

impl std::fmt::Display for MyError {
    fn fmt(
        &self,
        f: &mut std::fmt::Formatter<'_>,
    ) -> Result<(), std::fmt::Error> {
        write!(f, "{}", self.msg)
    }
}

fn example() -> Result<(), Box<dyn std::error::Error>> {
    let io_error = std::io::Error::new(std::io::ErrorKind::Other, "oh no!");
    Err(Box::new(MyError {
        msg: "Error message".to_string(),
        source: io_error,
    }))
}

fn main() {
    match example() {
        Ok(_) => {
            println!("Got OK");
        }
        Err(err) => {
            println!("Got {}", err);
        }
    }
}

miette

miette miette-crates.io miette-github miette-lib.rs cat-rust-patterns

miette is a fancy diagnostic reporting library and protocol.

mod mylib {
    //! library code: define unique error types and error wrappers

    use miette::Diagnostic;
    use miette::NamedSource;
    use miette::Result;
    use miette::SourceSpan;
    // You can derive a `Diagnostic` from any `std::error::Error` type.
    // `thiserror` plays nicely with `miette`
    use thiserror::Error;

    #[derive(Error, Diagnostic, Debug)]
    pub enum MyLibError {
        #[error("A bad thing happened!")] // provided by `thisError`
        #[diagnostic(
            // Use `#[diagnostic(code(...))]` to set the unique code for this error.
            code(my_lib::bad_thing),
            // Set the URL that will be displayed as an actual link in supported terminals.
            // `url(docsrs)` automatically create a link to this diagnostic on docs.rs
            // or use a custom URL like `url("https://my_website.com/error_codes#{}", self.code)`
            url(docsrs),
            // Supply help text
            help("try doing it better next time?"))]
        BadThingHappened,

        #[error("Something went wrong!")]
        SomethingWentWrong {
            // The Source that we're gonna be printing snippets out of.
            // This can be a String if you don't have or care about file names.
            #[source_code]
            src: NamedSource<String>,

            // Snippets and highlights can be included in the diagnostic!
            // You may also use `(usize, usize)`, the byte-offset and length
            // into an associated SourceCode or
            // `Option<SourceSpan>`
            #[label("This bit highlighted here is the problem")]
            bad_bit: SourceSpan,

            // Programmatically supply the help text
            #[help]
            advice: Option<String>, // Can also be `String`

            // Related errors
            #[related]
            others: Vec<MyLibError>,
        },

        // Wrap an Error
        #[error(transparent)]
        // Forward the source and Display methods
        // straight through to the underlying error.
        #[diagnostic(code(my_lib::io_error))]
        IoError(#[from] std::io::Error),

        // Wrap another Diagnostic
        // Use `#[diagnostic(transparent)]` to wrap another `[Diagnostic]`
        // You won't see labels otherwise
        #[error(transparent)]
        #[diagnostic(transparent)]
        AnotherError(#[from] AnotherError),
    }

    #[derive(Error, Diagnostic, Debug)]
    #[error("another error")]
    pub struct AnotherError {
        #[label("here")]
        pub at: SourceSpan,
    }

    pub fn this_fails() -> Result<()> {
        // You can use plain strings as a `Source`,
        // or anything that implements the one-method `Source` trait.
        let src = "source\n  text\n    here".to_string();
        // You may also use `map_err(|error| {
        // error.with_source_code(String::from("source code")) })` later.

        Err(MyLibError::SomethingWentWrong {
            src: NamedSource::new("bad_file.rs", src),
            bad_bit: (9, 4).into(),
            advice: Some("Some help text".to_string()),
            others: vec![MyLibError::BadThingHappened],
        })?;
        Ok(())
    }
}

use miette::Result;

/// To get errors printed nicely in application code, just return a
/// `Result<()>`
///
/// Note: You can swap out the default reporter for a
/// custom one using `miette::set_hook()`
fn main() -> Result<()> {
    mylib::this_fails()?;
    Ok(())
}

color-eyre

color-eyre color-eyre-crates.io color-eyre-github color-eyre-lib.rs

color-eyre is an error report handler for panics and eyre::Reports for colorful, consistent, and well formatted error reports for all kinds of errors.

It is a fork of anyhow that gives you more control over the format of the generated error messages. It is recommended if you intend to present error messages to end users. Otherwise anyhow is simpler.

fn main() {
    todo!();
}

See also

eyre

Do not use Error Chain⮳, which is deprecated.

File System

cat-filesystem

Dealing with files and file systems.

File Reading & Writing

Current Working Directory

RecipeCratesCategories
Get the current working directorystdcat-filesystem

User Directories

RecipeCratesCategories
dirsdirscat-filesystem
directoriesdirectoriescat-filesystem

Temporary Files and Directories

Directory Traversal

Walk the Filesystem while Respecting Ignore Files

File Watching

Read & Write

Read lines of strings from a file

std cat-filesystem

Writes a three-line message to a file, then reads it back a line at a time with the std::io::Lines⮳ iterator created by std::io::BufRead::linesstd::fs::File⮳ implements std::io::Read⮳ which provides std::io::BufReader⮳ trait. std::fs::File::create⮳ opens a std::fs::File⮳ for writing, std::fs::File::open⮳ for reading.

use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Error;
use std::io::Write;

fn main() -> Result<(), Error> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    let path = "temp/lines.txt";

    let mut output = File::create(path)?;
    write!(output, "Rust\n💖\nFun")?;

    let input = File::open(path)?;
    let buffered = BufReader::new(input);

    for line in buffered.lines() {
        println!("{}", line?);
    }

    Ok(())
}

Avoid writing and reading from the same file

same-file cat-filesystem

Use same_file::Handle⮳ to a file that can be tested for equality with other handles. In this example, the handles of file to be read from and to be written to are tested for equality.

use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Error;
use std::io::ErrorKind;
use std::path::Path;

use same_file::Handle;

fn main() -> Result<(), Error> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    std::fs::write("temp/new.txt", b"Lorem ipsum")?;

    let path_to_read = Path::new("temp/new.txt");
    let stdout_handle = Handle::stdout()?;
    let handle = Handle::from_path(path_to_read)?;

    if stdout_handle == handle {
        return Err(Error::new(
            ErrorKind::Other,
            "You are reading and writing to the same file",
        ));
    } else {
        let file = File::open(path_to_read)?;
        let file = BufReader::new(file);
        for (num, line) in file.lines().enumerate() {
            println!("{} : {}", num, line?.to_uppercase());
        }
    }

    Ok(())
}
cargo run

displays the contents of the file new.txt.

cargo run >> new.txt

errors because the two files are same.

Access a file randomly using a memory map

memmap2 cat-filesystem

Creates a memory map of a file using memmap2⮳ and simulates some non-sequential reads from the file. Using a memory map means you just index into a slice rather than dealing with std::fs::File::seek⮳ to navigate a std::fs::File⮳.

The memmap2::Mmap::map⮳ function assumes the file behind the memory map is not being modified at the same time by another process or else a race condition⮳ occurs.

use std::fs;
use std::fs::File;
use std::io::Error;
use std::io::Write;

use memmap2::Mmap;

fn main() -> Result<(), Error> {
    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    write!(
        File::create("temp/content.txt")?,
        "My hovercraft is full of eels!"
    )?;
    let file = File::open("temp/content.txt")?;
    let map = unsafe { Mmap::map(&file)? };
    println!("{:?}", map);
    let random_indexes = [0, 1, 2, 19, 22, 10, 11, 29];
    assert_eq!(&map[3..13], b"hovercraft");
    let random_bytes: Vec<u8> =
        random_indexes.iter().map(|&idx| map[idx]).collect();
    assert_eq!(&random_bytes[..], b"My loaf!");
    Ok(())
}

Current working directory

RecipeCratesCategories
Get the current working directorystdcat-filesystem

Get the current working directory

std cat-filesystem

use std::env;

use anyhow::Result;

fn main() -> Result<()> {
    let cwd = env::current_dir()?;
    println!("The current directory is {}", cwd.display());
    Ok(())
}

Directory Traversal

Find files that have been modified in the last 24 hours

std cat-filesystem

Gets the current working directory by calling std::env::current_dir⮳ then for each entries in std::fs::read_dir⮳ extracts the std::fs::DirEntry::path⮳ and gets the metadata via std::fs::Metadata⮳. The std::fs::Metadata::modified⮳ returns the std::time::SystemTime::elapsed⮳ time since last modification. std::time::Duration::as_secs⮳ converts the time to seconds and compared with 24 hours (24 60 60 seconds). std::fs::Metadata::is_file⮳ filters out directories.

use std::env;
use std::fs;

use anyhow::Result;
use anyhow::anyhow;

fn main() -> Result<()> {
    let current_dir = env::current_dir()?;
    println!(
        "Entries modified in the last 24 hours in {:?}:",
        current_dir
    );

    for entry in fs::read_dir(current_dir)? {
        let entry = entry?;
        let path = entry.path();
        let metadata = fs::metadata(&path)?;
        if let Ok(time) = metadata.modified() {
            // Note: SystemTime.elapsed can be flaky.
            if let Ok(duration) = time.elapsed() {
                let last_modified = duration.as_secs();
                if (last_modified < 24 * 3600) && metadata.is_file() {
                    println!(
                        "Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}",
                        last_modified,
                        metadata.permissions().readonly(),
                        metadata.len(),
                        path.file_name().ok_or(anyhow!("No filename"))?
                    );
                }
            }
        } else {
            println!("Last modification time not supported on this platform");
        }
    }
    Ok(())
}

Find loops for a given path

same-file same-file-crates.io same-file-github same-file-lib.rs

same-file is a simple crate for determining whether two file paths point to the same file.

Use same_file::is_same_file⮳ to detect loops for a given path. For example, a loop could be created on a Unix system via symlinks:

mkdir -p /tmp/foo/bar/baz
ln -s /tmp/foo/ /tmp/foo/bar/baz/qux

The following would assert that a loop exists.

#![cfg(target_os = "linux")]

use std::io;
use std::path::Path;
use std::path::PathBuf;

use same_file::is_same_file;

// Returns the two paths that form a loop, if found
// Returns None otherwise
// P: AsRef<Path> accepts PathBuf, Path...
fn contains_loop<P: AsRef<Path>>(
    path: P,
) -> io::Result<Option<(PathBuf, PathBuf)>> {
    let path: &Path = path.as_ref();
    // Copy into a mutable PathBuf
    let mut path_buf: PathBuf = path.to_path_buf();
    // Truncate path_buf in succession: /stuff/much -> /stuff -> /
    while path_buf.pop() {
        if is_same_file(&path_buf, path)? {
            return Ok(Some((path_buf, path.to_path_buf())));
            // Investigate the parent path against its own parents as well
        } else if let Some((looped_path1, looped_path2)) =
            contains_loop(&path_buf)?
        {
            return Ok(Some((looped_path1, looped_path2)));
        }
    }
    Ok(None)
}

fn main() {
    // `is_same_file` returns true if the two file paths may correspond to the
    // same file.
    assert!(is_same_file("/tmp/foo", "/tmp/./foo").unwrap_or(false));

    assert_eq!(
        contains_loop("/tmp/foo/bar/baz/qux/bar/baz").unwrap(),
        Some((
            PathBuf::from("/tmp/foo"),
            PathBuf::from("/tmp/foo/bar/baz/qux")
        ))
    );
    println!("Loop found.");
}

Recursively find duplicate file names

walkdir walkdir-crates.io walkdir-github walkdir-lib.rs cat-filesystem

Find recursively in the current directory duplicate filenames, printing them only once.

use std::collections::HashMap;

use walkdir::WalkDir;

fn main() {
    let mut filenames = HashMap::new();

    for entry in WalkDir::new(".")
        .into_iter()
        .filter_map(Result::ok)
        .filter(|e| !e.file_type().is_dir())
    {
        let f_name = String::from(entry.file_name().to_string_lossy());
        let counter = filenames.entry(f_name.clone()).or_insert(0);
        *counter += 1;

        if *counter == 2 {
            println!("{}", f_name);
        }
    }
}

Recursively find all files with a given predicate

walkdir walkdir-crates.io walkdir-github walkdir-lib.rs cat-filesystem

Find files modified within the last day in the current directory. Using walkdir::WalkDir::follow_links⮳ ensures symbolic links are followed like they were normal directories and files.

use anyhow::Result;
use walkdir::WalkDir;

fn main() -> Result<()> {
    for entry in WalkDir::new(".")
        .follow_links(true)
        .into_iter()
        .filter_map(|e| e.ok())
    {
        let f_name = entry.file_name().to_string_lossy();

        // `metadata()` can return errors for path values that the program
        // does not have permissions to access or if the path no longer exists.
        if let Ok(metadata) = entry.metadata() {
            let sec = metadata.modified()?;
            if let Ok(elapsed) = sec.elapsed() {
                if elapsed.as_secs() < 86400 {
                    println!("{}", f_name);
                }
            }
        }
        // You may also check for specific extensions:
        // && f_name.ends_with(".json")
    }

    Ok(())
}

Traverse directories while skipping dotfiles

walkdir walkdir-crates.io walkdir-github walkdir-lib.rs cat-filesystem

Uses walkdir::IntoIter::filter_entry⮳ to descend recursively into entries passing the is_not_hidden predicate thus skipping hidden files and directories. std::iter::Iterator::filter⮳ applies to each walkdir::IntoIter::filter_entry⮳ even if the parent is a hidden directory.

Root dir "." yields through walkdir::WalkDir::depth usage in is_not_hidden predicate.

use walkdir::DirEntry;
use walkdir::WalkDir;

fn is_not_hidden(entry: &DirEntry) -> bool {
    entry
        .file_name()
        .to_str()
        .map(|s| entry.depth() == 0 || !s.starts_with('.'))
        .unwrap_or(false)
}

fn main() {
    let w = WalkDir::new(".");
    w.into_iter()
        .filter_entry(is_not_hidden)
        .filter_map(|v| v.ok())
        .for_each(|x| println!("{}", x.path().display()));
}

Recursively calculate file sizes at a given depth

walkdir walkdir-crates.io walkdir-github walkdir-lib.rs cat-filesystem

Recursion depth can be flexibly set by walkdir::Walkdir::min_depth⮳ & walkdir::WalkDir::max_depth⮳ methods. Calculates sum of all file sizes to 3 subfolders depth, ignoring files in the root folder.

use walkdir::WalkDir;

fn main() {
    let total_size = WalkDir::new(".")
        .min_depth(1)
        .max_depth(3)
        .into_iter()
        .filter_map(|entry| entry.ok())
        .filter_map(|entry| entry.metadata().ok())
        .filter(|metadata| metadata.is_file())
        .fold(0, |acc, m| acc + m.len());

    println!("Total size: {} bytes.", total_size);
}

Find all files with a given extension recursively

glob glob-crates.io glob-github glob-lib.rs cat-filesystem

Recursively find all PNG files in the current directory. In this case, the ** pattern matches the current directory and all subdirectories.

Use the ** pattern in any path portion. For example, /media/**/*.png matches all PNGs in media and it's subdirectories.

use anyhow::Result;
use glob::glob;

fn main() -> Result<()> {
    for entry in glob("**/*.png")? {
        println!("{}", entry?.display());
    }

    Ok(())
}

Find all files with given pattern, ignoring filename case

glob glob-crates.io glob-github glob-lib.rs cat-filesystem

Find all image files in the /media/ directory matching the img_[0-9][0-9]*.png pattern.

A custom glob::MatchOptions⮳ struct is passed to the glob::glob_with⮳ function making the glob pattern case insensitive while keeping the other options std::default::Default⮳.

use anyhow::Result;
use glob::MatchOptions;
use glob::glob_with;

fn main() -> Result<()> {
    let options = MatchOptions {
        case_sensitive: false,
        ..Default::default()
    };

    for entry in glob_with("/media/img_[0-9]*.png", options)? {
        println!("{}", entry?.display());
    }

    Ok(())
}

Walk the filesystem

Walk the filesystem while respecting ignore files

ignore ignore-crates.io ignore-github ignore-lib.rs

ignore is a library for efficiently matching ignore files such as .gitignore against file paths.

Recursive filesystem walking that respects ignore files (like .gitignore)

fn main() {
    todo!();
}

File watching

Watch files or directories and execute a function when they change

notify-website notify notify-crates.io notify-github notify-lib.rs cat-filesystem

notify is a cross-platform filesystem notification library.


use std::path::Path;

use notify::EventHandler;
use notify::RecursiveMode;
use notify::Result;
use notify::Watcher;
use notify::event::Event;

/// Prints received events
struct EventPrinter;

impl EventHandler for EventPrinter {
    fn handle_event(&mut self, res_event: Result<Event>) {
        match res_event {
            Ok(event) => println!("event: {:?}", event),
            Err(e) => println!("watch error: {:?}", e),
        }
    }
}

fn main() -> Result<()> {
    // Automatically select the best implementation for your platform.
    let mut watcher = notify::recommended_watcher(EventPrinter)?;

    // Add a path to be watched. All files and directories at that path
    // and below will be monitored for changes.
    watcher.watch(Path::new("."), RecursiveMode::Recursive)?;

    Ok(())
}

Temporary files and directories

Create temporary files or temporary directories

tempfile-website tempfile tempfile-crates.io tempfile-github tempfile-lib.rs

tempfile supports both temporary files and temporary directories.

fn main() {
    todo!();
}

User directories

RecipeCratesCategories
dirsdirscat-filesystem
directoriesdirectoriescat-filesystem

Get platform-specific locations for configuration, cache, and other data

dirs

dirs dirs-crates.io dirs-github dirs-lib.rs

dirs is a low-level library that provides platform-specific standard locations of directories for config, cache and other data on Linux, Windows, macOS and Redox by leveraging the mechanisms defined by the XDG base/user directory specifications on Linux, the Known Folder API on Windows, and the Standard Directory guidelines on macOS.

fn main() {
    todo!();
}

directories

directories directories-crates.io directories-github directories-lib.rs

directories is a mid-level library that provides platform-specific standard locations of directories for config, cache and other data on Linux, Windows and macOS by leveraging the mechanisms defined by the XDG base/user directory specifications on Linux, the Known Folder API on Windows, and the Standard Directory guidelines on macOS.

directories is a higher-level library than dirs and can also compute paths for applications.

fn main() {
    todo!();
}

Hardware Support

cat-hardware-support

Interface with specific CPU or other hardware features.

Processor

Processor

Check the number of logical cpu cores

num_cpus num_cpus-crates.io num_cpus-github num_cpus-lib.rs cat-hardware-support

Shows the number of logical CPU cores in the current machine using num_cpus::get⮳.

fn main() {
    println!("Number of logical cores is {}", num_cpus::get());
}

Mathematics

cat-mathematics

Crates with a mathematical aspect.

Linear algebra

Trigonometry

Complex numbers

Statistics

Additional numeric types

Linear Algebra

Add matrices

ndarray ndarray-crates.io ndarray-github ndarray-lib.rs cat-data-structures cat-science

Creates two 2-D matrices with ndarray::arr2⮳ and sums them element-wise.

Note that the sum is computed as let sum = &a + &b. The & operator is used to avoid consuming a and b, making them available later for display. A new array is created containing their sum.

use ndarray::arr2;

fn main() {
    let a = arr2(&[[1, 2, 3], [4, 5, 6]]);

    let b = arr2(&[[6, 5, 4], [3, 2, 1]]);

    let sum = &a + &b;

    println!("{}", a);
    println!("+");
    println!("{}", b);
    println!("=");
    println!("{}", sum);
}

Multiply matrices

ndarray ndarray-crates.io ndarray-github ndarray-lib.rs cat-data-structures cat-science

Creates two matrices with ndarray::arr2⮳ and performs matrix multiplication on them with ndarray::ArrayBase::dot⮳.

use ndarray::arr2;

fn main() {
    let a = arr2(&[[1, 2, 3], [4, 5, 6]]);

    let b = arr2(&[[6, 3], [5, 2], [4, 1]]);

    println!("{}", a.dot(&b));
}

Multiply a scalar with a vector and a matrix

ndarray ndarray-crates.io ndarray-github ndarray-lib.rs cat-data-structures cat-science

Creates a 1-D array (vector) with ndarray::arr1⮳ and a 2-D array (matrix) with ndarray::arr2

First, a scalar is multiplied by the vector to get another vector. Then, the matrix is multiplied by the new vector with ndarray::Array2::dot⮳ (Matrix multiplication is performed using ndarray::Array2::dot⮳, while the * operator performs element-wise multiplication.)

In ndarray⮳, 1-D arrays can be interpreted as either row or column vectors depending on context. If representing the orientation of a vector is important, a 2-D array with one row or one column must be used instead. In this example, the vector is a 1-D array on the right-hand side, so ndarray::Array2::dot⮳ handles it as a column vector.

use ndarray::Array1;
use ndarray::arr1;
use ndarray::arr2;

fn main() {
    let scalar = 4;

    let vector = arr1(&[1, 2, 3]);

    let matrix = arr2(&[[4, 5, 6], [7, 8, 9]]);

    let new_vector: Array1<_> = scalar * vector;
    println!("{}", new_vector);

    let new_matrix = matrix.dot(&new_vector);
    println!("{}", new_matrix);
}

Compare vectors

ndarray ndarray-crates.io ndarray-github ndarray-lib.rs cat-data-structures cat-science

The ndarray⮳ crate supports a number of ways to create arrays -- this recipe creates ndarray::Array⮳ from std::Vec using std::convert::From⮳. Then, it sums the arrays element-wise.

This recipe contains an example of comparing two floating-point vectors element-wise. Floating-point numbers are often stored inexactly, making exact comparisons difficult. However, the approx::assert_abs_diff_eq⮳ macro from the approx⮳ crate allows for convenient element-wise comparisons. To use the approx⮳ crate with ndarray⮳, the approx⮳ feature must be added to the ndarray⮳ dependency in Cargo.toml. For example, ndarray = { version = "0.13", features = [ "approx" ] }.

This recipe also contains additional ownership examples. Here, let z = a + b consumes a and b, updates a with the result, then moves ownership to z. Alternatively, let w = &c + &d creates a new vector without consuming c or d, allowing their modification later. See Binary Operators With Two Arrays⮳ for additional detail.

use approx::assert_abs_diff_eq;
use ndarray::Array;

fn main() {
    let a = Array::from(vec![1., 2., 3., 4., 5.]);
    let b = Array::from(vec![5., 4., 3., 2., 1.]);
    let mut c = Array::from(vec![1., 2., 3., 4., 5.]);
    let mut d = Array::from(vec![5., 4., 3., 2., 1.]);

    let z = a + b;
    let w = &c + &d;

    assert_abs_diff_eq!(z, Array::from(vec![6., 6., 6., 6., 6.]));

    println!("c = {}", c);
    c[0] = 10.;
    d[1] = 10.;

    assert_abs_diff_eq!(w, Array::from(vec![6., 6., 6., 6., 6.]));
}

Calculate vector norms

ndarray ndarray-crates.io ndarray-github ndarray-lib.rs cat-data-structures cat-science

This recipe demonstrates use of the ndarray::Array1⮳ type, ndarray::Array1⮳ type, ndarray::ArrayBase::fold method, and ndarray::ArrayBase::dot⮳ method in computing the l1⮳ and l2⮳ norms of a given vector.

  • The l2_norm⮳ function is the simpler of the two, as it computes the square root of the dot product of a vector with itself. + The l1_norm⮳ function is computed by a ndarray::ArrayBase::fold⮳ operation that sums the absolute values of the elements. (This could also be performed with x.mapv(f64::abs).scalar_sum(), but that would allocate a new array for the result of the mapv.)

Note that both l1_norm⮳ and l2_norm⮳ take the ndarray::ArrayView1⮳ type. This recipe considers vector norms, so the norm functions only need to accept one-dimensional views, hence ndarray::ArrayView1⮳. While the functions could take a parameter of type &Array1<f64> instead, that would require the caller to have a reference to an owned array, which is more restrictive than just having access to a view (since a view can be created from any array or view, not just an owned array).

ndarray::Array⮳ and ndarray::Array⮳ are both type aliases for ndarray::Array⮳. So, the most general argument type for the caller would be &ArrayBase<S, Ix1> where S: Data, because then the caller could use &array or &view instead of x.view(). If the function is part of a public API, that may be a better choice for the benefit of users. For internal functions, the more concise ArrayView1<f64> may be preferable.

use ndarray::Array1;
use ndarray::ArrayView1;
use ndarray::array;

fn l1_norm(x: ArrayView1<f64>) -> f64 {
    x.fold(0., |acc, elem| acc + elem.abs())
}

fn l2_norm(x: ArrayView1<f64>) -> f64 {
    x.dot(&x).sqrt()
}

fn normalize(mut x: Array1<f64>) -> Array1<f64> {
    let norm = l2_norm(x.view());
    x.mapv_inplace(|e| e / norm);
    x
}

fn main() {
    let x = array![1., 2., 3., 4., 5.];
    println!("||x||_2 = {}", l2_norm(x.view()));
    println!("||x||_1 = {}", l1_norm(x.view()));
    println!("Normalizing x yields {:?}", normalize(x));
}

Invert a matrix

nalgebra nalgebra-crates.io nalgebra-github nalgebra-lib.rs cat-mathematics cat-no-std cat-science cat-wasm

Creates a 3x3 matrix with nalgebra::Matrix3⮳ and inverts it, if possible.

use nalgebra::Matrix3;

fn main() {
    let m1 = Matrix3::new(2.0, 1.0, 1.0, 3.0, 2.0, 1.0, 2.0, 1.0, 2.0);
    println!("m1 = {}", m1);
    match m1.try_inverse() {
        Some(inv) => {
            println!("The inverse of m1 is: {}", inv);
        }
        None => {
            println!("m1 is not invertible!");
        }
    }
}

(De)serialize a matrix

ndarray ndarray-crates.io ndarray-github ndarray-lib.rs cat-data-structures cat-science

Serialize and deserialize a matrix to and from JSON. Serialization is taken care of by serde_json::to_string⮳ and serde_json::to_string⮳ performs deserialization.

Note that serialization followed by deserialization gives back the original matrix.

use nalgebra::DMatrix;

fn main() -> Result<(), std::io::Error> {
    let row_slice: Vec<i32> = (1..5001).collect();
    let matrix = DMatrix::from_row_slice(50, 100, &row_slice);
    println!("{}", matrix);

    // serialize matrix
    let serialized_matrix = serde_json::to_string(&matrix)?;

    // deserialize matrix
    let deserialized_matrix: DMatrix<i32> =
        serde_json::from_str(&serialized_matrix)?;

    // verify that `deserialized_matrix` is equal to `matrix`
    assert!(deserialized_matrix == matrix);

    Ok(())
}

Trigonometry

Calculate the side length of a triangle

std cat-science

Calculates the length of the hypotenuse of a right-angle triangle with an angle of 2 radians and opposite side length of 80.

fn main() {
    let angle: f64 = 2.0;
    let side_length = 80.0;

    let hypotenuse = side_length / angle.sin();

    println!("Hypotenuse: {}", hypotenuse);
}

Verify that tan is equal to sin divided by cos

std cat-science

Verifies tan(x) is equal to sin(x)/cos(x) for x = 6.

fn main() {
    let x: f64 = 6.0;

    let a = x.tan();
    let b = x.sin() / x.cos();

    println!("a: {a}, b: {b}");
    assert_eq!(a, b);
}

Calculate the distance between two points on Earth

std

By default, Rust provides mathematical float methods⮳ such as trigonometric functions, square root, conversion functions between radians and degrees, and so forth.

The following example computes the distance in kilometers between two points on the Earth with the Haversine⮳ formula. Points are expressed as pairs of latitude and longitude in degrees. Then, to_radians⮳ converts them in radians. sincospowi⮳ and sqrt⮳ compute the central angle. Finally, it's possible to calculate the distance.

fn main() {
    let earth_radius_kilometer = 6371.0_f64;
    let (paris_latitude_degrees, paris_longitude_degrees) =
        (48.85341_f64, -2.34880_f64);
    let (london_latitude_degrees, london_longitude_degrees) =
        (51.50853_f64, -0.12574_f64);

    let paris_latitude = paris_latitude_degrees.to_radians();
    let london_latitude = london_latitude_degrees.to_radians();

    let delta_latitude =
        (paris_latitude_degrees - london_latitude_degrees).to_radians();
    let delta_longitude =
        (paris_longitude_degrees - london_longitude_degrees).to_radians();

    let central_angle_inner = (delta_latitude / 2.0).sin().powi(2)
        + paris_latitude.cos()
            * london_latitude.cos()
            * (delta_longitude / 2.0).sin().powi(2);
    let central_angle = 2.0 * central_angle_inner.sqrt().asin();

    let distance = earth_radius_kilometer * central_angle;

    println!(
        "Distance between Paris and London on the surface of Earth is {:.1} kilometers",
        distance
    );
}

Complex numbers

Create complex numbers

num num-crates.io num-github num-lib.rs cat-science cat-algorithms cat-no-std cat-data-structures

Creates complex numbers of type num::complex::Complex⮳. Both the real and imaginary part of the complex number must be of the same type.

fn main() {
    let complex_integer = num::complex::Complex::new(10, 20);
    let complex_float = num::complex::Complex::new(10.1, 20.1);

    println!("Complex integer: {}", complex_integer);
    println!("Complex float: {}", complex_float);
}

Add complex numbers

num num-crates.io num-github num-lib.rs cat-science cat-algorithms cat-no-std cat-data-structures

Performing mathematical operations on complex numbers is the same as on built-in types: the numbers in question must be of the same type (i.e. floats or integers).

fn main() {
    let complex_num1 = num::complex::Complex::new(10.0, 20.0); // Must use floats
    let complex_num2 = num::complex::Complex::new(3.1, -4.2);

    let sum = complex_num1 + complex_num2;

    println!("Sum: {}", sum);
}

Use mathematical functions on complex numbers

num num-crates.io num-github num-lib.rs cat-science cat-algorithms cat-no-std cat-data-structures

Complex numbers have a range of interesting properties when it comes to how they interact with other mathematical functions, most notibly the family of sine functions as well as the number e. To use these functions with complex numbers, the Complex type has a few built in functions, all of which can be found here: num::complex::Complex⮳.

use std::f64::consts::PI;

use num::complex::Complex;

fn main() {
    let x = Complex::new(0.0, 2.0 * PI);

    println!("e^(2i * pi) = {}", x.exp()); // =~1
}

Statistics

Calculate measures of central tendency

std cat-science

These examples calculate measures of central tendency for a data set contained within a Rust array. There may be no mean, median or mode to calculate for an empty set of data, so each function returns an std::option::Option⮳ to be handled by the caller.

The first example calculates the mean (the sum of all measurements divided by the number of measurements in the set) by producing an iterator of references over the data, and using std::iter::Iterator::sum⮳ and len⮳ to determine the total value and count of values respectively.

fn main() {
    let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];

    let sum = data.iter().sum::<i32>() as f32;
    let count = data.len();

    let mean = match count {
        positive if positive > 0 => Some(sum / count as f32),
        _ => None,
    };

    println!("Mean of the data is {:?}", mean);
}

The second example calculates the median using the quickselect algorithm, which avoids a full sort⮳ by sorting only partitions of the data set known to possibly contain the median. This uses std::cmp::Ord::cmp⮳ and std::cmp::Ordering⮳ to succinctly decide the next partition to examine, and split_at⮳ to choose an arbitrary pivot for the next partition at each step.

use std::cmp::Ordering;

fn partition(data: &[i32]) -> Option<(Vec<i32>, i32, Vec<i32>)> {
    match data.len() {
        0 => None,
        _ => {
            let (pivot_slice, tail) = data.split_at(1);
            let pivot = pivot_slice[0];
            let (left, right) =
                tail.iter().fold((vec![], vec![]), |mut splits, next| {
                    {
                        let &mut (ref mut left, ref mut right) = &mut splits;
                        if next < &pivot {
                            left.push(*next);
                        } else {
                            right.push(*next);
                        }
                    }
                    splits
                });

            Some((left, pivot, right))
        }
    }
}

fn select(data: &[i32], k: usize) -> Option<i32> {
    let part = partition(data);

    match part {
        None => None,
        Some((left, pivot, right)) => {
            let pivot_idx = left.len();

            match pivot_idx.cmp(&k) {
                Ordering::Equal => Some(pivot),
                Ordering::Greater => select(&left, k),
                Ordering::Less => select(&right, k - (pivot_idx + 1)),
            }
        }
    }
}

fn median(data: &[i32]) -> Option<f32> {
    let size = data.len();

    match size {
        even if even % 2 == 0 => {
            let fst_med = select(data, (even / 2) - 1);
            let snd_med = select(data, even / 2);

            match (fst_med, snd_med) {
                (Some(fst), Some(snd)) => Some((fst + snd) as f32 / 2.0),
                _ => None,
            }
        }
        odd => select(data, odd / 2).map(|x| x as f32),
    }
}

fn main() {
    let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];

    let part = partition(&data);
    println!("Partition is {:?}", part);

    let sel = select(&data, 5);
    println!("Selection at ordered index {} is {:?}", 5, sel);

    let med = median(&data);
    println!("Median is {:?}", med);
}

The final example calculates the mode using a mutable std::collections::HashMap⮳ to collect counts of each distinct integer from the set, using a std::iter::Iterator::fold⮳ and the std::collections::hash_map::Entry⮳ API. The most frequent value in the std::collections::HashMap⮳ surfaces with std::iter::Iterator::max_by_key⮳.

use std::collections::HashMap;

fn main() {
    let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];

    let frequencies = data.iter().fold(HashMap::new(), |mut freqs, value| {
        *freqs.entry(value).or_insert(0) += 1;
        freqs
    });

    let mode = frequencies
        .into_iter()
        .max_by_key(|&(_, count)| count)
        .map(|(value, _)| *value);

    println!("Mode of the data is {:?}", mode);
}

Compute the standard deviation

std cat-science

This example calculates the standard deviation and z-score of a set of measurements.

The standard deviation is defined as the square root of the variance (here calculated with f32's sqrt⮳ where the variance is the std::iter::Iterator::sum⮳ of the squared difference between each measurement and the mean divided by the number of measurements).

The z-score is the number of standard deviations a single measurement spans away from the mean of the data set.

fn mean(data: &[i32]) -> Option<f32> {
    let sum = data.iter().sum::<i32>() as f32;
    let count = data.len();

    match count {
        positive if positive > 0 => Some(sum / count as f32),
        _ => None,
    }
}

fn std_deviation(data: &[i32]) -> Option<f32> {
    match (mean(data), data.len()) {
        (Some(data_mean), count) if count > 0 => {
            let variance = data
                .iter()
                .map(|value| {
                    let diff = data_mean - (*value as f32);

                    diff * diff
                })
                .sum::<f32>()
                / count as f32;

            Some(variance.sqrt())
        }
        _ => None,
    }
}

fn main() {
    let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];

    let data_mean = mean(&data);
    println!("Mean is {:?}", data_mean);

    let data_std_deviation = std_deviation(&data);
    println!("Standard deviation is {:?}", data_std_deviation);

    let zscore = match (data_mean, data_std_deviation) {
        (Some(mean), Some(std_deviation)) => {
            let diff = data[4] as f32 - mean;

            Some(diff / std_deviation)
        }
        _ => None,
    };
    println!(
        "Z-score of data at index 4 (with value {}) is {:?}",
        data[4], zscore
    );
}

Additional numeric types

Abstract over different number types

num-traits num-traits-crates.io num-traits-github num-traits-lib.rs cat-algorithms cat-science cat-no-std

Numeric traits for generic mathematics. Traits like Number, Add, etc that allow you write functions that are generic over the specific numeric type

fn main() {
    todo!();
}

Use big integers

num

num num-crates.io num-github num-lib.rs cat-science cat-algorithms cat-no-std cat-data-structures

A collection of numeric types and traits for Rust, including bigint, complex, rational, range iterators, generic integers, and more! Calculation for integers exceeding 128 bits are possible with num::BigInt⮳.

use num::bigint::BigInt;
use num::bigint::ToBigInt;

fn factorial(x: i32) -> BigInt {
    if let Some(mut factorial) = 1.to_bigint() {
        for i in 1..=x {
            factorial *= i;
        }
        factorial
    } else {
        panic!("Failed to calculate factorial!");
    }
}

fn main() {
    println!("{}! equals {}", 100, factorial(100));
}

num-bigint

num-bigint num-bigint-crates.io num-bigint-github num-bigint-lib.rs cat-science cat-algorithms cat-data-structures

Big integer implementation for Rust. "It's not the fastest, but it's part of the trusted num library."

fn main() {
    todo!();
}

rug

rug rug-crates.io rug-github rug-lib.rs cat-api-bindings cat-mathematics

Arbitrary-precision integers, rational, floating-point and complex numbers based on GMP, MPFR and MPC. LGPL licensed. Wrapper for GMP. Much faster than num-bigint.

fn main() {
    todo!();
}

Use big decimals

rust_decimal rust_decimal-crates.io rust_decimal-github rust_decimal-lib.rs cat-data-structures cat-mathematics cat-science

Decimal number implementation written in pure Rust suitable for financial and fixed-precision calculations. The binary representation consists of a 96 bit integer number, a scaling factor used to specify the decimal fraction and a 1 bit sign.

fn main() {
    todo!();
}

Sort floats

ordered-float ordered-float-crates.io ordered-float-github ordered-float-lib.rs cat-no-std cat-rust-patterns cat-science

Wrappers for total ordering on floats. Float types that don't allow NaN and are therefore orderable. You can also use the total_cmp method from the standard library like .sort_by(|a, b| a.total_cmp(&b)).

fn main() {
    todo!();
}

Memory Management

cat-memory-management

Deal with allocation, memory mapping, garbage collection, reference counting, or interfaces to foreign memory managers.

RecipeCratesCategories
Declare lazily evaluated constantslazy_staticcat-caching cat-rust-patterns cat-memory-management
RecipeCratesCategories
stdstdcat-memory-management
once_cellstd once_cellcat-memory-management
lazy_staticlazy_staticcat-memory-management

Global static

RecipeCratesCategories
Declare lazily evaluated constantslazy_staticcat-caching cat-rust-patterns cat-memory-management

Declare lazily evaluated constants

lazy_static lazy_static-crates.io lazy_static-github lazy_static-lib.rs cat-caching cat-rust-patterns cat-memory-management

Declares a lazily evaluated constant std::collections::HashMap⮳. The std::collections::HashMap⮳ will be evaluated once and stored behind a global static reference.

use std::collections::HashMap;

use lazy_static::lazy_static;

lazy_static! {
    static ref PRIVILEGES: HashMap<&'static str, Vec<&'static str>> = {
        let mut map = HashMap::new();
        map.insert("James", vec!["user", "admin"]);
        map.insert("Jim", vec!["user"]);
        map
    };
}

fn show_access(name: &str) {
    let access = PRIVILEGES.get(name);
    println!("{}: {:?}", name, access);
}

fn main() {
    let access = PRIVILEGES.get("James");
    println!("James: {:?}", access);

    show_access("Jim");
}

Lazy Initialization

RecipeCratesCategories
stdstdcat-memory-management
once_cellstd once_cellcat-memory-management
lazy_staticlazy_staticcat-memory-management

Two key libraries:

  • once_cell: newer crate with more ergonomic API. Should be preferred for all new projects.
  • lazy_static: older crate. API is less convenient, but crate is stable and maintained.

The core functionality of once_cell is now included in the standard library with the remaining parts on track to be stabilised in future.

std

std cat-memory-management

OnceCell⮳ is a cell which can be written to only once.

The corresponding Sync version of OnceCell<T> is OnceLock<T>.

use std::cell::OnceCell;

fn main() {
    let cell = OnceCell::new();
    assert!(cell.get().is_none());

    let value: &String = cell.get_or_init(|| "Hello, World!".to_string());
    println!("{value}");
    assert_eq!(value, "Hello, World!");
    assert!(cell.get().is_some());
}

once_cell

once_cell once_cell-crates.io once_cell-github once_cell-lib.rs cat-memory-management cat-rust-patterns

once_cell⮳ provides two cell-like types, unsync::OnceCell and sync::OnceCell. A OnceCell might store arbitrary non-Copy types, can be assigned to at most once and provides direct access to the stored contents. The sync flavor is thread-safe. once_cell also has a once_cell::sync::Lazy⮳ type, build on top of OnceCell⮳:

use std::collections::HashMap;
use std::sync::Mutex;

use once_cell::sync::Lazy;

// Must be static, not const
static GLOBAL_DATA: Lazy<Mutex<HashMap<i32, String>>> = Lazy::new(|| {
    let mut m = HashMap::new();
    m.insert(13, "Spica".to_string());
    m.insert(74, "Hoyten".to_string());
    Mutex::new(m)
});

fn main() {
    println!("{:?}", GLOBAL_DATA.lock().unwrap());
}

lazy_static

lazy_static lazy_static-crates.io lazy_static-github lazy_static-lib.rs cat-no-std cat-memory-management cat-rust-patterns

fn main() {
    todo!();
}

Operating System

cat-os

Bindings to operating system-specific APIs.

External

Low-level system calls

RecipeCratesCategories
Call libc, the C standard librarylibccat-os

Operating Systems written in Rust

External Command

Run an external command and process its stdout

std regex cat-os cat-text-processing

Runs git log --oneline as an external std::process::Command⮳ and inspects its std::process::Output⮳ using regex::Regex⮳ to get the hash and message of the last 5 commits.

use std::process::Command;

use anyhow::Result;
use anyhow::bail;
use regex::Regex;

#[derive(PartialEq, Default, Clone, Debug)]
struct Commit {
    hash: String,
    message: String,
}

fn main() -> Result<()> {
    let output = Command::new("git").arg("log").arg("--oneline").output()?;

    if !output.status.success() {
        bail!("Command executed with failing error code");
    }

    let pattern = Regex::new(
        r"(?x)
        ([0-9a-fA-F]+) # commit hash
        (.*)           # The commit message",
    )?;

    String::from_utf8(output.stdout)?
        .lines()
        .filter_map(|line| pattern.captures(line))
        .map(|cap| Commit {
            hash: cap[1].to_string(),
            message: cap[2].trim().to_string(),
        })
        .take(5)
        .for_each(|x| println!("{:?}", x));

    Ok(())
}

Run an external command, passing it stdin, then check for an error code

std cat-os

Opens the python interpreter using an external std::process::Command⮳ and passes it a python statement for execution. The std::process::Output⮳ of statement is then parsed.

use std::collections::HashSet;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;

use anyhow::Result;
use anyhow::anyhow;
use anyhow::bail;

fn main() -> Result<()> {
    let mut child = Command::new("rev")
        .stdin(Stdio::piped())
        .stderr(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()?;

    child
        .stdin
        .as_mut()
        .ok_or(anyhow!("Child process' stdin has not been captured!"))?
        .write_all(b"1234 56789")?;

    let output = child.wait_with_output()?;

    if output.status.success() {
        let raw_output = String::from_utf8(output.stdout)?;
        let words = raw_output
            .split_whitespace()
            .map(|s| s.to_lowercase())
            .collect::<HashSet<_>>();
        println!("Found {} unique words:", words.len());
        println!("{:#?}", words);
    } else {
        let err = String::from_utf8(output.stderr)?;
        bail!("External command failed:\n {}", err);
    }
    Ok(())
}

Run piped external commands

std cat-os

Shows up to the 10th biggest files and subdirectories in the current working directory. It is equivalent to running: du -ah . | sort -hr | head -n 10.

std::process::Command⮳ represent a process. Output of a child process is captured with a std::process::Stdio::piped⮳ between parent and child.

#![allow(clippy::single_match)]
#![cfg(target_family = "unix")]

fn main() -> anyhow::Result<()> {
    use std::process::Command;
    use std::process::Stdio;

    let directory = std::env::current_dir()?;
    let mut du_output_child = Command::new("du")
        .arg("-ah")
        .arg(&directory)
        .stdout(Stdio::piped())
        .spawn()?;

    if let Some(du_output) = du_output_child.stdout.take() {
        let mut sort_output_child = Command::new("sort")
            .arg("-hr")
            .stdin(du_output)
            .stdout(Stdio::piped())
            .spawn()?;

        du_output_child.wait()?;

        match sort_output_child.stdout.take() {
            Some(sort_output) => {
                let head_output_child = Command::new("head")
                    .args(["-n", "10"])
                    .stdin(sort_output)
                    .stdout(Stdio::piped())
                    .spawn()?;

                let head_stdout = head_output_child.wait_with_output()?;

                sort_output_child.wait()?;

                println!(
                    "Top 10 biggest files and directories in '{}':\n{}",
                    directory.display(),
                    String::from_utf8(head_stdout.stdout).unwrap()
                );
            }
            _ => {}
        }
    }

    Ok(())
}

Redirect both the stdout and stderr of a child process to the same file

std cat-os

Spawns a child process and redirects std::io::Stdout⮳ and std::io::Stderr⮳ to the same file. It follows the same idea as run piped external commands, however std::process::Stdio⮳ writes to a specified file. std::fs::File::try_clone⮳ references the same file handle for std::io::Stdout⮳ and std::io::Stderr⮳. It will ensure that both handles write with the same cursor position.

The below recipe is equivalent to run the Unix shell command ls . oops >out.txt 2>&1.


fn main() -> Result<(), std::io::Error> {
    use std::fs;
    use std::fs::File;
    use std::process::Command;
    use std::process::Stdio;

    if !fs::exists("temp")? {
        fs::create_dir("temp")?;
    }
    let outputs = File::create("temp/out.txt")?;
    let errors = outputs.try_clone()?;

    Command::new("ls")
        .args([".", "oops"])
        .stdout(Stdio::from(outputs))
        .stderr(Stdio::from(errors))
        .spawn()?
        .wait_with_output()?;

    Ok(())
}

Continuously process the outputs of a child process

std cat-os

In Run an external command and process its stdout, processing doesn't start until the external std::process::Command is finished. The recipe below calls std::process::Stdio::piped to create a pipe, and reads std::io::Stdout⮳ continuously as soon as the std::io::BufReader⮳ is updated.

The below recipe is equivalent to the Unix shell command journalctl | grep usb.

use std::io::BufRead;
use std::io::BufReader;
use std::io::Error;
use std::io::ErrorKind;
use std::process::Command;
use std::process::Stdio;

fn main() -> Result<(), Error> {
    // NOTE: `systemd` should be installed for this example to work.
    let stdout = Command::new("journalctl")
        .stdout(Stdio::piped())
        .spawn()?
        .stdout
        .ok_or_else(|| {
            Error::new(ErrorKind::Other, "Could not capture standard output.")
        })?;

    let reader = BufReader::new(stdout);

    reader
        .lines()
        .map_while(Result::ok)
        .filter(|line| line.contains("usb"))
        .for_each(|line| println!("{}", line));

    Ok(())
}

Read an environment variable

std cat-os

Reads an environment variable via std::env::var⮳.

use std::env;
use std::fs;
use std::io::Error;

fn main() -> Result<(), Error> {
    // Read `config_path` from the environment variable `CONFIG`.
    // If `CONFIG` isn't set, fall back to a default config path.
    let config_path =
        env::var("CONFIG").unwrap_or("/etc/subversion/config".to_string());

    let config: String = fs::read_to_string(config_path)?;
    println!("Config: {}", config);

    Ok(())
}

Run child processes using duct

duct duct-crates.io duct-github duct-lib.rs

duct⮳ is a library for running child processes. duct makes it easy to build pipelines and redirect I/O like a shell. At the same time, duct helps you write correct, portable code: whitespace is never significant, errors from child processes get reported by default, and a variety of gotchas, bugs, and platform inconsistencies⮳ are handled for you.

Low-level interaction with the operating system

RecipeCratesCategories
Call libc, the C standard librarylibccat-os

Call libc, the C standard library

libc libc-crates.io libc-github libc-lib.rs cat-external-ffi-bindings cat-os cat-no-std

Bindings for directly calling libc functions.

fn main() {
    todo!();
}

Operating systems written in Rust

Host containers with bottlerocket

bottlerocket bottlerocket-crates.io c-bottlerocket-github bottlerocket-lib.rs

bottlerocket⮳ is an operating system designed for hosting containers.

Run a Rust operating system on your computer with Redox

Redox(github)⮳ is an experimental, Unix-like, general-purpose, microkernel-based operating system written in Rust, aiming to bring Rust to a modern microkernel, a full set of programs, and be a complete alternative to Linux and BSD.

Rust Patterns

cat-rust-patterns

Shared solutions for particular situations specific to programming in Rust.

Handle errors

Customize errors

RecipeCratesCategories
anyhowanyhowcat-rust-patterns
thisErrorthiserrorcat-rust-patterns
miettemiettecat-rust-patterns
color-eyrecolor-eyrecat-rust-patterns

Rust design patterns

Functional programming

RecipeCratesCategories
Compose iteratorsitertoolscat-rust-patterns

Rust idioms

RecipeCratesCategories
Rust idioms and patterns{{#crate }}cat-rust-patterns

Design Patterns

Implement an abstract factory

abstract-factory in rust

Clone a struct storing a boxed trait object

dyn-clone dyn-clone-crates.io dyn-clone-github dyn-clone-lib.rs cat-rust-patterns cat-no-std

The dyn-clone crate provides a DynClone trait that can be used in trait objects, and a clone_box function that can clone any sized or dynamically sized implementation of DynClone. Types that implement the standard library’s std::clone::Clone trait are automatically usable by a DynClone trait object.

use dyn_clone::DynClone;

trait MyTrait: DynClone {
    fn recite(&self);
}

impl MyTrait for String {
    fn recite(&self) {
        println!("{} ♫", self);
    }
}

fn main() {
    let line = "The slithy structs did gyre and gimble the namespace";

    // Build a trait object holding a String.
    // This requires String to implement MyTrait and std::clone::Clone.
    let x: Box<dyn MyTrait> = Box::new(String::from(line));

    x.recite();

    // The type of x2 is a Box<dyn MyTrait> cloned from x.
    let x2 = dyn_clone::clone_box(&*x);

    x2.recite();
}

Functional programming

RecipeCratesCategories
Compose iteratorsitertoolscat-rust-patterns

Compose iterators

itertools itertools-crates.io itertools-github itertools-lib.rs cat-algorithms cat-rust-patterns cat-no-std cat-no-std::no-alloc

itertools includes extra iterator adaptors, functions and macros.

fn main() {
    use itertools::Itertools;
    use itertools::assert_equal;
    use itertools::chain;

    // Assert that two iterables produce equal sequences
    assert_equal("hello world".split(' '), "hello world".split(' '));

    // Chain
    let mut result: Vec<i32> = Vec::new();

    for element in chain(&[1, 2, 3], &[4]) {
        result.push(*element);
    }
    assert_eq!(result, vec![1, 2, 3, 4]);

    // Cloned
    assert_eq!(itertools::cloned(b"abc").next(), Some(b'a'));

    // Deduplicate
    let data = vec![1., 1., 2., 3., 3., 2., 2.];
    itertools::assert_equal(data.into_iter().dedup(), vec![1., 2., 3., 2.]);

    // `into_group_map`
    let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)];
    let lookup = data.into_iter().into_group_map();

    assert_eq!(lookup[&0], vec![10, 20]);
    assert_eq!(lookup.get(&1), None);
    assert_eq!(lookup[&2], vec![12, 42]);
    assert_eq!(lookup[&3], vec![13, 33])
}

Rust idioms and patterns

RecipeCratesCategories
Rust idioms and patterns{{#crate }}cat-rust-patterns

See also

Builder derive crates

RecipeCratesCategories
bonboncat-rust-patterns
derive_builderderive_buildercat-rust-patterns
typed-buildertyped-buildercat-rust-patterns

typed-builder

typed-builder typed-builder-crates.io typed-builder-github typed-builder-lib.rs cat-rust-patterns

Compile-time type-checked builder derive. The oldest crate for compile-time-checked builders that has (987K downloads/month, 916 stars, 7 years old)

derive_builder

derive_builder derive_builder-crates.io derive_builder-github derive_builder-lib.rs cat-development-tools cat-rust-patterns

Rust macro to automatically implement the builder pattern for arbitrary structs.

runtime-checked builders, works with &self, &mut self builder patterns. The oldest crate for runtime-checked builders overall (1,58M downloads/month, 1285 stars, 8 years old)

bon

bon-website bon bon-crates.io bon-github bon-lib.rs cat-asynchronous cat-data-structures cat-no-std cat-no-std::no-alloc cat-rust-patterns

Next-gen compile-time-checked builder generator, named function arguments.

bon - compile-time-checked builders, named function arguments via builders (foo().arg(...).call()), fallible/async builders, method builders (self.foo(...).arg(...).call()). The newest crate built based on lessons learned from typed-builder and derive_builder (33K downloads/month, but gaining popularity, 1095 stars, 3 months old).

Template Engine

cat-template-engine

Crates designed to combine templates with data to produce result documents, usually with an emphasis on processing text.

Create HTML files from a template

RecipeCratesCategories
Create HTML files from a templateteracat-template-engine

Create Markdown fragments from a template

Tera

RecipeCratesCategories
Create HTML files from a templateteracat-template-engine

Create HTML files from a template

tera-website tera tera-crates.io tera-github tera-lib.rs cat-template-engine

tera is a template engine based on Jinja2/Django templates.

fn main() {
    todo!();
}

Tinytemplate

Create Markdown fragments from a template

tinytemplate tinytemplate-crates.io tinytemplate-github tinytemplate-lib.rs cat-template-engine

tinytemplate is a simple, lightweight template engine.

fn main() {
    todo!();
}

Text Editors

cat-text-editors

Applications for editing text.

IDEs

VS CodeInstall the rust-analyzer extension. Also consider the CodeLLDB, Dependi and Even Better TOML
RustRoverIt is available for free for non-commercial use.
IntelliJ / CLion JetBrains IDE + Rust pluginIf you have a JetBrains license, CLion is your go-to editor for Rust in JetBrains’ IDE suite
Zedzed is available for macOS, Linux, and soon for Windows. Written in Rust.
Helix editor(github)Install the rust-analyzer binary
vi, vim, or NeovimConfigure Vim for Rust
Emacs⮳ (or derivatives like Doom Emacs, Spacemacs⮳, etc.)Configure emacs for Rust
Sublime Text"Rust enhanced" package
Visual StudioConfigure rust-analyzer for Visual Studio
LapceOpen source, written in Rust
Xcode
Eclipse CorrosionIt provides development tools for Rust and Cargo inside the Eclipse IDE
AtomIt has been sunset

Write Rust code with VS Code

Rust plugin for VS Code

rust-analyzer

Write Rust code with the zed editor

zed zed zed-crates.io zed-github zed-lib.rs

See also

Six IDEs built for Rust

Text Processing

Deal with the complexities of human language when expressed in textual form.

cat-text-processing

Find, extract, and replace text using regular expressions

Parse strings

Concatenate strings

RecipeCratesCategories
Compare string concatenation methodsstdcat-text-processing

Regular Expressions

Verify and extract login from an email address

regex lazy_static cat-text-processing

Validates that an email address is formatted correctly, and extracts everything before the @ symbol.

use lazy_static::lazy_static;
use regex::Regex;

fn extract_login(input: &str) -> Option<&str> {
    lazy_static! {
        static ref RE: Regex = Regex::new(
            r"(?x)
            ^(?P<login>[^@\s]+)@
            ([[:word:]]+\.)*
            [[:word:]]+$
            "
        )
        .unwrap();
    }
    RE.captures(input)
        .and_then(|cap| cap.name("login").map(|login| login.as_str()))
}

fn main() {
    let login = extract_login(r"I❤email@example.com");
    println!("{:?}", login);
    assert_eq!(login, Some(r"I❤email"));

    let login = extract_login(r"sdf+sdsfsd.as.sdsd@jhkk.d.rl");
    println!("{:?}", login);
    assert_eq!(login, Some(r"sdf+sdsfsd.as.sdsd"));

    assert_eq!(extract_login(r"More@Than@One@at.com"), None);
    assert_eq!(extract_login(r"Not an email@email"), None);
}

Extract a list of unique #hashtags from a text

regex lazy_static cat-text-processing

Extracts, sorts, and deduplicates list of hashtags from text.

The hashtag regex given here only catches Latin hashtags that start with a letter. The complete Twitter hashtag regextwitter-hashtag-regex is much more complicated.

use std::collections::HashSet;

use lazy_static::lazy_static;
use regex::Regex;

fn extract_hashtags(text: &str) -> HashSet<&str> {
    lazy_static! {
        static ref HASHTAG_REGEX: Regex =
            Regex::new(r"\#[a-zA-Z][0-9a-zA-Z_]*").unwrap();
    }
    HASHTAG_REGEX
        .find_iter(text)
        .map(|mat| mat.as_str())
        .collect()
}

fn main() {
    let tweet = "Hey #world, I just got my new #dog, say hello to Till. #dog #forever #2 #_ ";
    let tags = extract_hashtags(tweet);
    println!("{:?}", tags);
    assert!(
        tags.contains("#dog")
            && tags.contains("#forever")
            && tags.contains("#world")
    );
    assert_eq!(tags.len(), 3);
}

Extract phone numbers from text

regex cat-text-processing

Processes a string of text using regex::Regex::captures_iter⮳ to capture multiple phone numbers. The example here is for US convention phone numbers.

use std::fmt;

use anyhow::Result;
use regex::Regex;

struct PhoneNumber<'a> {
    area: &'a str,
    exchange: &'a str,
    subscriber: &'a str,
}

impl fmt::Display for PhoneNumber<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "1 ({}) {}-{}", self.area, self.exchange, self.subscriber)
    }
}

fn main() -> Result<()> {
    let phone_text = "
    +1 505 881 9292 (v) +1 505 778 2212 (c) +1 505 881 9297 (f)
    (202) 991 9534
    Alex 5553920011
    1 (800) 233-2010
    1.299.339.1020";

    let re = Regex::new(
        r#"(?x)
          (?:\+?1)?                       # Country Code Optional
          [\s\.]?
          (([2-9]\d{2})|\(([2-9]\d{2})\)) # Area Code
          [\s\.\-]?
          ([2-9]\d{2})                    # Exchange Code
          [\s\.\-]?
          (\d{4})                         # Subscriber Number"#,
    )?;

    let phone_numbers = re.captures_iter(phone_text).filter_map(|cap| {
        let groups = (cap.get(2).or(cap.get(3)), cap.get(4), cap.get(5));
        match groups {
            (Some(area), Some(ext), Some(sub)) => Some(PhoneNumber {
                area: area.as_str(),
                exchange: ext.as_str(),
                subscriber: sub.as_str(),
            }),
            _ => None,
        }
    });

    assert_eq!(
        phone_numbers.map(|m| m.to_string()).collect::<Vec<_>>(),
        vec![
            "1 (505) 881-9292",
            "1 (505) 778-2212",
            "1 (505) 881-9297",
            "1 (202) 991-9534",
            "1 (555) 392-0011",
            "1 (800) 233-2010",
            "1 (299) 339-1020",
        ]
    );

    Ok(())
}

Filter a log file by matching multiple regular expressions

regex cat-text-processing

Reads a file named application.log and only outputs the lines containing “version X.X.X”, some IP address followed by port 443 (e.g. “192.168.0.1:443”), or a specific warning.

A regex::RegexSetBuilder⮳ composes a regex::RegexSetBuilder⮳ Since backslashes are very common in regular expressions, using raw string literals⮳ makes them more readable.

use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;

use anyhow::Result;
use regex::RegexSetBuilder;

fn main() -> Result<()> {
    let log_path = "temp/application.log";
    let buffered = BufReader::new(File::open(log_path)?);

    let set = RegexSetBuilder::new([
        r#"version "\d\.\d\.\d""#,
        r#"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:443"#,
        r#"warning.*timeout expired"#,
    ])
    .case_insensitive(true)
    .build()?;

    buffered
        .lines()                // yield instances of io::Result<String>
        .map_while(Result::ok)
        .filter(|line| set.is_match(line.as_str()))
        .for_each(|x| println!("{}", x));

    Ok(())
}

Replace all occurrences of one text pattern with another pattern

regex lazy_static cat-text-processing

Replaces all occurrences of the standard ISO 8601 YYYY-MM-DD date pattern with the equivalent American English date with slashes. For example 2013-01-15 becomes 01/15/2013.

The method regex::Regex::replace_all⮳ replaces all occurrences of the whole regex. &str implements the regex::Replacer⮳ trait which allows variables like $abcde to refer to corresponding named capture groups (?P<abcde>REGEX) from the search regex. See the replacement string syntax⮳ for examples and escaping detail.

use std::borrow::Cow;

use lazy_static::lazy_static;
use regex::Regex;

fn reformat_dates(before: &str) -> Cow<str> {
    lazy_static! {
        static ref ISO8601_DATE_REGEX: Regex =
            Regex::new(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})").unwrap();
    }
    ISO8601_DATE_REGEX.replace_all(before, "$m/$d/$y")
}

fn main() {
    let before = "2012-03-14, 2013-01-15 and 2014-07-05";
    let after = reformat_dates(before);
    println!("{}", after);
    assert_eq!(after, "03/14/2012, 01/15/2013 and 07/05/2014");
}

Use regular expressions with backreferences and lookarounds

fancy-regex fancy-regex-crates.io fancy-regex-github fancy-regex-lib.rs cat-text-processing

regex is the de facto standard regex library. It is very fast, but does not support fancier features such as backtracking, backreferences, and look-arounds. Use fancy-regex if you need features that regex doesn't support.

fn main() {
    todo!();
}

Longer Regex Example

regex regex-github cat-text-processing

use std::collections::BTreeMap;

use once_cell::sync::Lazy;
use regex::Regex;

// Regular expression and the names of its capture groups.
struct Re(Regex, Vec<&'static str>);

// Regexes take a while to compile; it is reasonable to store them in
// a global static
static GLOBAL_REGEX: Lazy<BTreeMap<&str, Re>> = Lazy::new(|| {
    println!("Initializing Regexes...\n");
    // A sorted map:
    let mut m = BTreeMap::new();
    // A Markdown inline link:
    // (?<name>  ) is a named capture group.
    // \s is a whitespace. \S is a not-whitespace.
    m.insert(
        "[text](http...)",
        Re(
            Regex::new(r"[^!]\[(?<text>.*?)\]\s?\(\s*?(?<link>\S*?)\s*?\)")
                .unwrap(),
            vec!["text", "link"],
        ),
    );
    // A Markdown autolink
    m.insert(
        "<http...>",
        Re(Regex::new(r"<(?<link>http.*?)>").unwrap(), vec!["link"]),
    );
    // A Markdown shortcut link
    // or <spaces>( or :
    m.insert(
        "[text] ...",
        Re(
            Regex::new(r"[^!\]]\[(?<text>[^\[\]]+?)\]\s*?[^\[\(:]").unwrap(),
            vec!["text"],
        ),
    );
    // A Markdown reference-style link
    m.insert(
        "[text][label]",
        Re(
            Regex::new(r"[^!\]]\[(?<text>.*?)\]\s?\[(?<label>.+?)\]").unwrap(),
            vec!["text", "label"],
        ),
    );
    // A Markdown reference definition (with optional title):
    // (?:  ) is a non-capturing group.
    // (?m) flags multi-line mode. ^ and $ are the beginning and end of a
    // line, respectively.
    m.insert(
        "[label]: url \"title\"",
        Re(Regex::new(r#"(?m)^\s*?\[(?<label>.*?)\]:\s*?(?<url>\S+)\s*?(?:"(?<title>.*)")?\s*$"#).unwrap(),
        vec!["label", "url", "title"])
    );
    m
});

#[allow(dead_code)]
fn extract_inline_links(contents: &str) {
    for (_, [text, link]) in GLOBAL_REGEX["[text](http...)"]
        .0
        // `captures_iter` iterates through `Captures`, which stores the
        // capture groups for each match.
        .captures_iter(contents)
        // `extract` returns a tuple where
        // the first element corresponds to the full substring of the contents
        // that matched the regex. The second element is an array of
        // substrings, with each corresponding to the substring that matched
        // for a particular capture group.
        .map(|c| c.extract())
    {
        println!("[{text}]({link})\n");
    }
}

// Locate markup in text
fn search_with_all_regexes(contents: &str) {
    // Try to match all reggular expressions
    for (key, re) in GLOBAL_REGEX.iter() {
        println!("----------------------\nLooking for {}:\n", key);
        for caps in re.0.captures_iter(contents) {
            // Print the whole match
            print!("{} -> ", &caps[0]);
            for group in re.1.iter() {
                print!(
                    "{}={}; ",
                    group,
                    // Retrieve each named capture group in turn...
                    // `extract` can't be used here, since the # of capture
                    // groups varies.
                    caps.name(group).map_or("", |m| m.as_str())
                );
            }
            println!("\n");
        }
    }
}

// Example Markdown to process
fn get_test_markdown() -> String {
    let md: &'static str = "
<http://url0/>

[text1](url1)

[text2][lbl2]

[lbl2]: url2 \"title2\"

[lbl3][]

[lbl4]

![image5](image_url5)

![image6][image_lbl6]

image_lbl6: image_url6

![image_lbl7]

![image_lbl8][]
";
    md.to_owned()
}

fn main() {
    search_with_all_regexes(get_test_markdown().as_str());
}

String Parsing

Collect unicode graphemes

unicode-segmentation unicode-segmentation-crates.io unicode-segmentation-github unicode-segmentation-lib.rs cat-text-processing

Collect individual Unicode graphemes from UTF-8 string using the unicode_segmentation::UnicodeSegmentation::graphemes⮳ function from the unicode_segmentation⮳ crate.

use unicode_segmentation::UnicodeSegmentation;

fn main() {
    let name = "José Guimarães\r\n";
    let graphemes =
        UnicodeSegmentation::graphemes(name, true).collect::<Vec<&str>>();
    println!("{:?}", graphemes);
    assert_eq!(graphemes[3], "é");
}

Implement the FromStr trait for a custom struct

std cat-text-processing

Creates a custom struct RGB and implements the FromStr trait to convert a provided color hex code into its RGB color code.

use std::str::FromStr;

#[derive(Debug, PartialEq)]
struct Rgb {
    r: u8,
    g: u8,
    b: u8,
}

impl FromStr for Rgb {
    type Err = std::num::ParseIntError;

    // Parses a color hex code of the form '#rRgGbB..' into an
    // instance of 'RGB'
    fn from_str(hex_code: &str) -> Result<Self, Self::Err> {
        // u8::from_str_radix(src: &str, radix: u32) converts a string
        // slice in a given base to u8
        let r: u8 = u8::from_str_radix(&hex_code[1..3], 16)?;
        let g: u8 = u8::from_str_radix(&hex_code[3..5], 16)?;
        let b: u8 = u8::from_str_radix(&hex_code[5..7], 16)?;

        Ok(Rgb { r, g, b })
    }
}

fn main() {
    let code: &str = r"#fa7268";
    match Rgb::from_str(code) {
        Ok(rgb) => {
            println!(
                r"The RGB color code is: R: {} G: {} B: {}",
                rgb.r, rgb.g, rgb.b
            );
        }
        Err(_) => {
            println!("{} is not a valid color hex code!", code);
        }
    }

    // test whether from_str performs as expected
    assert_eq!(Rgb::from_str(r"#fa7268").unwrap(), Rgb {
        r: 250,
        g: 114,
        b: 104
    });
}

String concatenation

cat-text-processing

RecipeCratesCategories
Compare string concatenation methodsstdcat-text-processing

Compare string concatenation methods

String concatenation benchmark

fn main() {
    todo!();
}

Web Programming

cat-web-programming

Create applications for the Web.

Manipulate uniform resource locations (URLs)

Handle media types (MIME)

Scrape Web Pages

See also

Are we Web yet?

Building a crawler in Rust: design and associated types

Extracting Links

reqwest select cat-network-programming cat-web-programming

Use reqwest::get⮳ to perform a HTTP GET request and then use select::document::Document::from_read⮳ to parse the response into a HTML document. select::document::Document::find⮳ with the criteria of select::predicate::Name⮳ is "a" retrieves all links. Call std-core::iter::Iterator::filter_map⮳ on the select::selection::Selection⮳ retrieves URLs from links that have the "href" select::node::Node::attr⮳ (attribute).

use anyhow::Result;
use select::document::Document;
use select::predicate::Name;

#[tokio::main]
async fn main() -> Result<()> {
    let res = reqwest::get("https://www.rust-lang.org/en-US/")
        .await?
        .text()
        .await?;

    Document::from(res.as_str())
        .find(Name("a"))
        .filter_map(|n| n.attr("href"))
        .for_each(|x| println!("{}", x));

    Ok(())
}

reqwest select url cat-network-programming cat-web-programming

Call get_base_url to retrieve the base URL. If the document has a base tag, get the href select::node::Node::attr⮳ from base tag. select::node::Node::attr⮳ of the original URL acts as a default.

Iterates through links in the document and creates a tokio::task::spawn⮳ task that will parse an individual link with url::ParseOptions⮳ and tokio::task::spawn⮳. The task makes a request to the links with reqwest⮳ and verifies reqwest::StatusCode⮳. Then the tasks await⮳ completion before ending the program.

use std::collections::HashSet;

use anyhow::Result;
use reqwest::StatusCode;
use select::document::Document;
use select::predicate::Name;
use url::Position;
use url::Url;

async fn get_base_url(url: &Url, doc: &Document) -> Result<Url> {
    let base_tag_href =
        doc.find(Name("base")).filter_map(|n| n.attr("href")).next();
    let base_url = base_tag_href
        .map_or_else(|| Url::parse(&url[..Position::BeforePath]), Url::parse)?;
    Ok(base_url)
}

async fn check_link(url: &Url) -> Result<bool> {
    let res = reqwest::get(url.as_ref()).await?;
    Ok(res.status() != StatusCode::NOT_FOUND)
}

#[tokio::main]
async fn main() -> Result<()> {
    let url = Url::parse("https://www.rust-lang.org/en-US/")?;
    let res = reqwest::get(url.as_ref()).await?.text().await?;
    let document = Document::from(res.as_str());
    let base_url = get_base_url(&url, &document).await?;
    let base_parser = Url::options().base_url(Some(&base_url));
    let links: HashSet<Url> = document
        .find(Name("a"))
        .filter_map(|n| n.attr("href"))
        .filter_map(|link| base_parser.parse(link).ok())
        .collect();
    let mut tasks = vec![];

    for link in links {
        tasks.push(tokio::spawn(async move {
            if check_link(&link).await.unwrap() {
                println!("{} is OK", link);
            } else {
                println!("{} is Broken", link);
            }
        }));
    }

    for task in tasks {
        task.await?
    }

    Ok(())
}

reqwest regex cat-network-programming cat-web-programming

Pull the source of a MediaWiki page using reqwest::get⮳ and then look for all entries of internal and external links with regex::Regex::captures_iter⮳. Using std::borrow::Cow⮳ avoids excessive std::string::String⮳ allocations.

MediaWiki link syntax is described here⮳.

use std::borrow::Cow;
use std::collections::HashSet;

use anyhow::Result;
use lazy_static::lazy_static;
use regex::Regex;

fn extract_links(content: &str) -> HashSet<Cow<str>> {
    lazy_static! {
        static ref WIKI_REGEX: Regex = Regex::new(
            r"(?x)
                \[\[(?P<internal>[^\[\]|]*)[^\[\]]*\]\]    # internal links
                |
                (url=|URL\||\[)(?P<external>http.*?)[ \|}] # external links
            "
        )
        .unwrap();
    }

    let links: HashSet<_> = WIKI_REGEX
        .captures_iter(content)
        .map(|c| match (c.name("internal"), c.name("external")) {
            (Some(val), None) => Cow::from(val.as_str().to_lowercase()),
            (None, Some(val)) => Cow::from(val.as_str()),
            _ => unreachable!(),
        })
        .collect();

    links
}

#[tokio::main]
async fn main() -> Result<()> {
    let content = reqwest::get(
    "https://en.wikipedia.org/w/index.php?title=Rust_(programming_language)&action=raw",
  )
  .await?
  .text()
  .await?;

    println!("{:#?}", extract_links(content.as_str()));

    Ok(())
}

Uniform Resource Location

Parse a URL from a string to a Url type

url cat-network-programming

The url::Url::parse⮳ method from the url⮳ crate validates and parses a &str into a url::Url⮳ struct. The input string may be malformed so this method returns Result<Url, ParseError>.

Once the URL has been parsed, it can be used with all of the methods in the url::Url⮳ type.

use url::ParseError;
use url::Url;

fn main() -> Result<(), ParseError> {
    let s = "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open";

    let parsed = Url::parse(s)?;
    println!("The path part of the URL is: {}", parsed.path());

    Ok(())
}

Create a base URL by removing path segments

url cat-network-programming

A base URL includes a protocol and a domain. Base URLs have no folders, files or query strings. Each of those items are stripped out of the given URL. url::PathSegmentsMut::clear⮳ removes paths and url::Url::set_query⮳ removes query string.

use anyhow::Result;
use anyhow::anyhow;
use url::Url;

fn base_url(mut url: Url) -> Result<Url> {
    match url.path_segments_mut() {
        Ok(mut path) => {
            path.clear();
        }
        Err(_) => {
            return Err(anyhow!("This URL is cannot-be-a-base."));
        }
    }

    url.set_query(None);

    Ok(url)
}

fn main() -> Result<()> {
    let full = "https://github.com/rust-lang/cargo?asdf";

    let url = Url::parse(full)?;
    let base = base_url(url)?;

    assert_eq!(base.as_str(), "https://github.com/");
    println!("The base of the URL is: {}", base);

    Ok(())
}

Create new URLs from a base URL

url cat-network-programming

The url::Url::join⮳ method creates a new URL from a base and relative path.

use url::ParseError;
use url::Url;

fn build_github_url(path: &str) -> Result<Url, ParseError> {
    const GITHUB: &str = "https://github.com";

    let base =
        Url::parse(GITHUB).expect("This hardcoded URL is known to be valid");
    let joined = base.join(path)?;

    Ok(joined)
}

fn main() -> Result<(), ParseError> {
    let path = "/rust-lang/cargo";

    let gh = build_github_url(path)?;

    assert_eq!(gh.as_str(), "https://github.com/rust-lang/cargo");
    println!("The joined URL is: {}", gh);

    Ok(())
}

Extract the URL origin (scheme / host / port)

url cat-network-programming

The url::Url⮳ struct exposes various methods to extract information about the URL it represents.

use url::Host;
use url::ParseError;
use url::Url;

fn main() -> Result<(), ParseError> {
    let s = "ftp://rust-lang.org/examples";

    let url = Url::parse(s)?;

    assert_eq!(url.scheme(), "ftp");
    assert_eq!(url.host(), Some(Host::Domain("rust-lang.org")));
    assert_eq!(url.port_or_known_default(), Some(21));
    println!("The origin is as expected!");

    Ok(())
}

url::Url::origin⮳ produces the same result.

use anyhow::Result;
use url::Host;
use url::Origin;
use url::Url;

fn main() -> Result<()> {
    let s = "ftp://rust-lang.org/examples";

    let url = Url::parse(s)?;

    let expected_scheme = "ftp".to_owned();
    let expected_host = Host::Domain("rust-lang.org".to_owned());
    let expected_port = 21;
    let expected = Origin::Tuple(expected_scheme, expected_host, expected_port);

    let origin = url.origin();
    assert_eq!(origin, expected);
    println!("The origin is as expected!");

    Ok(())
}

Remove fragment identifiers and query pairs from a URL

url cat-network-programming

Parses url::Url⮳ and slices it with url::Position⮳ to strip unneeded URL parts.

use url::ParseError;
use url::Position;
use url::Url;

fn main() -> Result<(), ParseError> {
    let parsed = Url::parse(
        "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open",
    )?;
    let cleaned: &str = &parsed[..Position::AfterPath];
    println!("`cleaned`: {}", cleaned);
    Ok(())
}

Media Types

Get a MIME type from a string

mime cat-encoding cat-web-programming

The following example shows how to parse a mime::Mime type from a string using the mime⮳ crate. mime::Mime⮳ produces a default mime::Mime⮳ type in an std::result::Result::unwrap_or⮳ clause.

use mime::APPLICATION_OCTET_STREAM;
use mime::Mime;

fn main() {
    let invalid_mime_type = "i n v a l i d";
    let default_mime = invalid_mime_type
        .parse::<Mime>()
        .unwrap_or(APPLICATION_OCTET_STREAM);

    println!(
        "MIME for {:?} used default value {:?}",
        invalid_mime_type, default_mime
    );

    let valid_mime_type = "TEXT/PLAIN";
    let parsed_mime = valid_mime_type
        .parse::<Mime>()
        .unwrap_or(APPLICATION_OCTET_STREAM);

    println!(
        "MIME for {:?} was parsed as {:?}",
        valid_mime_type, parsed_mime
    );
}

Get a MIME type from a filename

mime cat-encoding cat-web-programming

The following example shows how to return the correct MIME type from a given filename using the mime⮳ crate. The program will check for file extensions and match against a known list. The return value is mime::Mime⮳.

use mime::Mime;

fn find_mimetype(filename: &str) -> Mime {
    let parts: Vec<&str> = filename.split('.').collect();

    let res = match parts.last() {
        Some(v) => match *v {
            "png" => mime::IMAGE_PNG,
            "jpg" => mime::IMAGE_JPEG,
            "json" => mime::APPLICATION_JSON,
            &_ => mime::TEXT_PLAIN,
        },
        None => mime::TEXT_PLAIN,
    };
    res
}

fn main() {
    let filenames = vec!["foobar.jpg", "foo.bar", "foobar.png"];
    for file in filenames {
        let mime = find_mimetype(file);
        println!("MIME for {}: {}", file, mime);
    }
}

Parse the MIME type of a HTTP response

reqwest mime cat-network-programming cat-encoding cat-web-programming

When receiving a HTTP response from reqwest⮳ the MIME type⮳ or media type may be found in the Content-Type⮳ header. reqwest::header::HeaderMap::get⮳ retrieves the header as a reqwest::header::HeaderValue⮳ which can be converted to a string. The mime⮳ crate can then parse that, yielding a mime::Mime⮳ value.

The mime⮳ crate also defines some commonly used MIME types.

Note that the reqwest::header⮳ module is exported from the http⮳ crate.

use std::str::FromStr;

use anyhow::Result;
use mime::Mime;
use reqwest::header::CONTENT_TYPE;

#[tokio::main]
async fn main() -> Result<()> {
    let response =
        reqwest::get("https://www.rust-lang.org/logos/rust-logo-32x32.png")
            .await?;
    let headers = response.headers();

    match headers.get(CONTENT_TYPE) {
        None => {
            println!("The response does not contain a Content-Type header.");
        }
        Some(content_type) => {
            let content_type = Mime::from_str(content_type.to_str()?)?;
            let media_type =
                match (content_type.type_(), content_type.subtype()) {
                    (mime::TEXT, mime::HTML) => "a HTML document",
                    (mime::TEXT, _) => "a text document",
                    (mime::IMAGE, mime::PNG) => "a PNG image",
                    (mime::IMAGE, _) => "an image",
                    _ => "neither text nor image",
                };

            println!("The reponse contains {}.", media_type);
        }
    };

    Ok(())
}

Clients

cat-web-programming::http-client

Make HTTP network requests.

HTTP client libraries

RecipeCratesCategories
reqwestreqwestcat-web-programming::http-client
urequreqcat-web-programming::http-client
hyperhypercat-web-programming::http-client

Make HTTP requests

Call an API

Download and upload

HTTP clients

RecipeCratesCategories
reqwestreqwestcat-web-programming::http-client
urequreqcat-web-programming::http-client
hyperhypercat-web-programming::http-client

reqwest

reqwest reqwest-crates.io reqwest-github reqwest-lib.rs

reqwest is a full-fat HTTP client. It can be used in both synchronous and asynchronous code. It requires the tokio runtime.

fn main() {
    todo!();
}

ureq

ureq ureq-crates.io ureq-github ureq-lib.rs cat-web-programming::http-client

ureq is a minimal synchronous HTTP client, focused on simplicity and minimizing dependencies.

fn main() {
    todo!();
}

hyper

hyper hyper-crates.io hyper-github hyper-lib.rs

hyper is a low-level HTTP implementation (both client and server). It implements HTTP/1, and HTTP/2. It works best with the tokio async runtime, but can support other runtimes.

fn main() {
    todo!();
}

Make HTTP requests

Make a HTTP GET request

reqwest cat-network-programming cat-web-programming

Parses the supplied URL and makes a synchronous HTTP GET request with reqwest::blocking::get⮳ Prints obtained reqwest::blocking::Response⮳ status and headers. Reads HTTP response body into an allocated std::string::String⮳ using std::io::Read::read_to_string⮳.

use std::io::Read;

use anyhow::Result;

fn main() -> Result<()> {
    let mut res = reqwest::blocking::get("http://httpbin.org/get")?;
    let mut body = String::new();
    res.read_to_string(&mut body)?;

    println!("Status: {}", res.status());
    println!("Headers:\n{:#?}", res.headers());
    println!("Body:\n{}", body);

    Ok(())
}

Make a HTTP GET request asynchronously

reqwest tokio-website tokio tokio-crates.io tokio-github tokio-lib.rs cat-network-programming cat-web-programming cat-asynchronous

A similar approach can be used by including the tokio⮳ executor to make the main function asynchronous, retrieving the same information.

In this example, tokio::main⮳ handles all the heavy executor setup and allows sequential code implemented without blocking until .await.

Uses the asynchronous versions of reqwest⮳, both reqwest::get⮳ and reqwest::Response⮳.

use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let res = reqwest::get("http://httpbin.org/get").await?;
    println!("Status: {}", res.status());
    println!("Headers:\n{:#?}", res.headers());

    let body = res.text().await?;
    println!("Body:\n{}", body);
    Ok(())
}

Set custom headers and URL parameters for a REST request

reqwest hyper url cat-network-programming cat-web-programming cat-web-programming::http-client

Sets both standard and custom HTTP headers as well as URL parameters for a HTTP GET request. Creates a custom header of type XPoweredBy with hyper::header!⮳ macro.

Builds complex URL with url::Url::parse_with_params⮳. Sets standard headers hyper::header::USER_AGENThyper::header::AUTHORIZATION⮳ and custom XPoweredBy with reqwest::RequestBuilder::header⮳, then makes the request with reqwest::RequestBuilder::send⮳.

The request targets http://httpbin.org/headers service which responds with a JSON dict containing all request headers for easy verification.

use std::collections::HashMap;

use anyhow::Result;
use reqwest::header;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
pub struct HeadersEcho {
    pub headers: HashMap<String, String>,
}

fn main() -> Result<()> {
    // Parse an absolute URL from a string and add params to its query string
    let url = url::Url::parse_with_params("http://httpbin.org/headers", &[
        ("lang", "rust"),
        ("browser", "servo"),
    ])?;

    // Define default headers for all requests
    let mut default_headers = header::HeaderMap::new();
    default_headers
        .insert("X-MY-HEADER", header::HeaderValue::from_static("value"));

    let client = reqwest::blocking::Client::builder()
        .user_agent("Rust-test")
        .default_headers(default_headers)
        .build()?;

    // Headers for this request only
    let mut headers = header::HeaderMap::new();
    headers.insert(
        reqwest::header::CONTENT_TYPE,
        header::HeaderValue::from_static("image/png"),
    );

    let response = client.get(url)
        .headers(headers)
        .bearer_auth("DEadBEEfc001cAFeEDEcafBAd") // Enable HTTP bearer authentication.
        .send()?;

    assert_eq!(
        response.url().as_str(),
        "http://httpbin.org/headers?lang=rust&browser=servo"
    );

    let out: HeadersEcho = response.json()?;
    assert_eq!(
        out.headers["Authorization"],
        "Bearer DEadBEEfc001cAFeEDEcafBAd"
    );
    assert_eq!(out.headers["User-Agent"], "Rust-test");
    assert_eq!(out.headers["X-My-Header"], "value");
    println!("{:?}", out);
    Ok(())
}

Call an API

Query the GitHub API

reqwest serde cat-network-programming cat-encoding cat-web-programming cat-web-programming::http-client

Queries GitHub stargazers API v3⮳ with reqwest::get⮳ to get list of all users who have marked a GitHub project with a star. reqwest::Response⮳ is deserialized with reqwest::Response::json⮳ into User objects implementing serde::Deserialize⮳.

tokio::main is used to set up the async executor and the process waits for reqwest::get to complete before processing the response into User instances.

use reqwest::Error;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct User {
    login: String,
    id: u32,
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    let request_url = format!(
        "https://api.github.com/repos/{owner}/{repo}/stargazers",
        owner = "john-cd",
        repo = "rust_howto"
    );
    println!("{}", request_url);

    let client = reqwest::Client::builder().user_agent("Rust-test").build()?;

    let response = client.get(&request_url).send().await?;

    let users: Vec<User> = response.json().await?;
    println!("{:?}", users);
    Ok(())
}

Check if an API resource exists

reqwest cat-network-programming cat-web-programming cat-web-programming::http-client

Query the GitHub Users Endpoint using a HEAD request reqwest::Client::head⮳ and then inspect the response code to determine success. This is a quick way to query a rest resource without needing to receive a body. reqwest::Client⮳ configured with reqwest::ClientBuilder::timeout⮳ ensures a request will not last longer than a timeout.

Due to both reqwest::ClientBuilder::build⮳ and reqwest::RequestBuilder::send⮳ returning reqwest::Error⮳ types, the shortcut reqwest::Result⮳ is used for the main function return type.

use std::time::Duration;

use reqwest::ClientBuilder;

#[tokio::main]
async fn main() -> reqwest::Result<()> {
    let user = "ferris-the-crab";
    let request_url = format!("https://api.github.com/users/{}", user);
    println!("{}", request_url);

    let timeout = Duration::new(5, 0);
    let client = ClientBuilder::new().timeout(timeout).build()?;
    let response = client.head(&request_url).send().await?;

    if response.status().is_success() {
        println!("{} is a user!", user);
    } else {
        println!("{} is not a user!", user);
    }

    Ok(())
}

Create and delete a Gist with the GitHub API

reqwest serde cat-network-programming cat-encoding cat-web-programming cat-web-programming::http-client

Creates a gist with POST request to GitHub gists API v3⮳ using reqwest::Client::post⮳ and removes it with DELETE request using reqwest::Client::post⮳.

The reqwest::Client⮳ is responsible for details of both requests including URL, body and authentication. The POST body from reqwest::Client⮳ macro provides arbitrary JSON body. Call to reqwest::Client⮳ sets the request body. reqwest::Client⮳ handles authentication. The call to reqwest::Client⮳ synchronously executes the requests.

use std::collections::HashMap;
use std::env;

use anyhow::Result;
use reqwest::Client;
use serde::Deserialize;
use serde::Serialize;

#[derive(Deserialize, Serialize, Debug)]
struct Post<'a> {
    description: &'a str,
    public: bool,
    files: HashMap<&'a str, Content<'a>>,
}

#[derive(Deserialize, Serialize, Debug)]
struct Content<'a> {
    content: &'a str,
}

#[derive(Deserialize, Debug)]
struct Gist {
    id: String,
    html_url: String,
}

#[tokio::main]
async fn main() -> Result<()> {
    let gh_user = env::var("GH_USER")?;
    let gh_pass = env::var("GH_PASS")?;

    // Example POST to the GitHub gists API
    let gist_body = Post {
        description: "the description for this gist",
        public: true,
        files: {
            let mut h = HashMap::new();
            h.insert("main.rs", Content {
                content: r#"
fn main() { println!("hello world!");}
"#,
            });
            h
        },
    };

    let request_url = "https://api.github.com/gists";
    let response = Client::new()
        .post(request_url)
        .basic_auth(gh_user.clone(), Some(gh_pass.clone()))
        .json(&gist_body)
        .send()
        .await?;

    let gist: Gist = response.json().await?;
    println!("Created {:?}", gist);

    let request_url = format!("{}/{}", request_url, gist.id);
    let response = Client::new()
        .delete(&request_url)
        .basic_auth(gh_user, Some(gh_pass))
        .send()
        .await?;

    println!(
        "Gist {} deleted! Status code: {}",
        gist.id,
        response.status()
    );
    Ok(())
}

The example uses HTTP basic auth⮳ in order to authorize access to GitHub API⮳. Typical use case would employ one of the much more complex OAuth⮳ authorization flows.

Consume a paginated RESTful API

reqwest serde cat-network-programming cat-encoding cat-web-programming cat-web-programming::http-client

Wraps a paginated web API in a convenient Rust iterator. The iterator lazily fetches the next page of results from the remote server as it arrives at the end of each page.

// version_id then join version_id and Version.id ro retrieve crate
// name Consider a simpler API for example purposes

// use anyhow::Result;
// use serde::Deserialize;

// // Structs used to deserialize the JSON produced by the API

// #[derive(Deserialize)]
// struct ApiResponse {
//     dependencies: Vec<Dependency>,
//     meta: Meta,
//     versions: Vec<Version>,
// }

// // https://github.com/rust-lang/crates.io/issues/856
// #[derive(Deserialize)]
// struct Dependency {
//     version_id: String,
// }

// #[derive(Deserialize)]
// struct Meta {
//     total: u32,
// }

// #[derive(Deserialize)]
// struct Version {
//     id: String,
//     #[serde(alias = "crate")]
//     crate_id: String,
// }

// // Main struct

// struct ReverseDependencies {
//     crate_id: String,
//     dependencies: <Vec<Dependency> as IntoIterator>::IntoIter,
//     client: reqwest::blocking::Client,
//     page: u32,
//     per_page: u32,
//     total: u32,
// }

// impl ReverseDependencies {
//     fn of(crate_id: &str) -> Result<Self> {
//         let client = reqwest::blocking::Client::builder()
//             .user_agent("Rust-test")
//             .build()?;
//         Ok(ReverseDependencies {
//             crate_id: crate_id.to_owned(),
//             dependencies: vec![].into_iter(),
//             client,
//             page: 0,
//             per_page: 100,
//             total: 0,
//         })
//     }

//     fn try_next(&mut self) -> Result<Option<Dependency>> {
//         if let Some(dep) = self.dependencies.next() {
//             return Ok(Some(dep));
//         }

//         if self.page > 0 && self.page * self.per_page >= self.total
// {             return Ok(None);
//         }

//         self.page += 1;
//         let url = format!(
//             "https://crates.io/api/v1/crates/{}/reverse_dependencies?page={}&per_page={}",
//             self.crate_id, self.page, self.per_page
//         );
//         println!("Calling {}", url);
//         let resp = self.client.get(url).send()?;
//         //println!("{:#?}", resp);
//         //let text = resp.text()?;
//         //println!("{:#?}", text);
//         let json = resp.json::<ApiResponse>()?;
//         self.dependencies = json.dependencies.into_iter();
//         self.total = json.meta.total;
//         Ok(self.dependencies.next())
//     }
// }

// impl Iterator for ReverseDependencies {
//     type Item = Result<Dependency>;

//     fn next(&mut self) -> Option<Self::Item> {
//         match self.try_next() {
//             Ok(Some(dep)) => Some(Ok(dep)),
//             Ok(None) => None,
//             Err(err) => Some(Err(err)),
//         }
//     }
// }

// fn main() -> Result<()> {
//     for dep in ReverseDependencies::of("bit-vec")? {
//         println!("reverse dependency: {}", dep?.crate_id);
//     }
//     Ok(())
// }

fn main() -> anyhow::Result<()> {
    Ok(())
}

Handle a rate-limited API

reqwest hyper cat-network-programming cat-web-programming cat-web-programming::http-client

This example uses the GitHub API - rate limiting⮳, as an example of how to handle remote server errors. This example uses the hyper::header!⮳ macro to parse the response header and checks for reqwest::StatusCode::FORBIDDEN⮳ If the response exceeds the rate limit, the example waits and retries.

// use std::thread;
// use std::time::Duration;
// use std::time::UNIX_EPOCH;

// use anyhow::anyhow;
// use anyhow::Result;
// use reqwest::StatusCode;

// fn main() -> Result<()> {
//     let url = "https://api.github.com/users/john-cd";
//     let client = reqwest::blocking::Client::new();

//     loop {
//         let response = client.get(url).send()?;
//         let hdrs = response.headers();

//         let rate_limit: usize =
// hdrs.get("x-ratelimit-limit").ok_or_else( || {
// anyhow!("response doesn't include the expected x-ratelimit-limit
// header") }         )?.to_str()?.parse()?;

//         let rate_remaining: usize =
// hdrs.get("x-ratelimit-remaining")         .ok_or_else(|| {
// anyhow!("response doesn't include the expected
// x-ratelimit-remaining header") })?.to_str()?.parse()?;

//         let rate_reset_at: u64 = hdrs.get("x-ratelimit-reset")
//         .ok_or_else(|| { anyhow!("response doesn't include the
// expected x-ratelimit-reset header") })?.to_str()?.parse()?;

//         let rate_reset_within =
//             Duration::from_secs(rate_reset_at) -
// UNIX_EPOCH.elapsed()?;

//         if response.status() == StatusCode::FORBIDDEN &&
// rate_remaining == 0 {             println!("Sleeping for {}
// seconds.", rate_reset_within.as_secs());
// thread::sleep(rate_reset_within);             continue;
//         } else {
//             println!(
//                 "Rate limit is currently {}/{}, the reset of this
// limit will be within {} seconds.",                 rate_remaining,
//                 rate_limit,
//                 rate_reset_within.as_secs(),
//             );
//             break;
//         }
//     }
//     Ok(())
// }

fn main() -> anyhow::Result<()> {
    Ok(())
}

Download and upload

Download a file to a temporary directory

reqwest tempfile cat-network-programming cat-filesystem cat-web-programming

Creates a temporary directory with tempfile::Builder⮳ and downloads a file over HTTP using reqwest::get⮳ asynchronously.

Creates a target std::fs::File⮳ with name obtained from reqwest::Response::url⮳ within tempfile::Builder::tempdir⮳ and copies downloaded data into it with std::io::copy⮳. The temporary directory is automatically removed on program exit.

use std::fs::File;
use std::io::copy;

use anyhow::Result;
use tempfile::Builder;

#[tokio::main]
async fn main() -> Result<()> {
    let tmp_dir = Builder::new().prefix("example").tempdir()?;
    let target = "https://www.rust-lang.org/logos/rust-logo-512x512.png";
    let response = reqwest::get(target).await?;

    let mut dest = {
        let fname = response
            .url()
            .path_segments()
            .and_then(|segments| segments.last())
            .and_then(|name| if name.is_empty() { None } else { Some(name) })
            .unwrap_or("tmp.bin");

        println!("file to download: '{}'", fname);
        let fname = tmp_dir.path().join(fname);
        println!("will be located under: '{:?}'", fname);
        File::create(fname)?
    };
    let content = response.text().await?;
    copy(&mut content.as_bytes(), &mut dest)?;
    Ok(())
}

POST a file to paste.rs

reqwest cat-network-programming cat-web-programming cat-web-programming::http-client

reqwest::Client⮳ establishes a connection to https://paste.rs following the reqwest::RequestBuilder⮳ pattern. Calling reqwest::Client::post⮳ with a URL establishes the destination, reqwest::RequestBuilder::body⮳ sets the content to send by reading the file, and reqwest::RequestBuilder::send⮳ blocks until the file uploads and the response returns. std::io::Read::read_to_string⮳ returns the response and displays in the console.

use std::fs::File;
use std::io::Read;

use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
    let mut file = File::open("temp/message")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    let paste_api = "https://paste.rs";
    let client = reqwest::Client::new();
    let res = client.post(paste_api).body(contents).send().await?;
    let response_text = res.text().await?;
    println!("Your paste is located at: {}", response_text);
    Ok(())
}

Make a partial download with HTTP range headers

reqwest cat-network-programming cat-web-programming cat-web-programming::http-client

Uses reqwest::blocking::Client::head⮳ to get the Content-Length⮳ of the response.

The code then uses reqwest::blocking::Client::get⮳ to download the content in chunks of 10240 bytes, while printing progress messages. This exmple uses the synchronous reqwest module. The Range⮳ header specifies the chunk size and position.

The Range header is defined in RFC7233⮳.

use std::fs::File;
use std::str::FromStr;

use anyhow::Result;
use anyhow::anyhow;
use anyhow::bail;
use reqwest::StatusCode;
use reqwest::header::CONTENT_LENGTH;
use reqwest::header::HeaderValue;
use reqwest::header::RANGE;

struct PartialRangeIter {
    start: u64,
    end: u64,
    buffer_size: u32,
}

impl PartialRangeIter {
    pub fn new(start: u64, end: u64, buffer_size: u32) -> Result<Self> {
        if buffer_size == 0 {
            Err(anyhow!(
                "invalid buffer_size, give a value greater than zero."
            ))?;
        }
        Ok(PartialRangeIter {
            start,
            end,
            buffer_size,
        })
    }
}

impl Iterator for PartialRangeIter {
    type Item = HeaderValue;

    fn next(&mut self) -> Option<Self::Item> {
        if self.start > self.end {
            None
        } else {
            let prev_start = self.start;
            self.start += std::cmp::min(
                self.buffer_size as u64,
                self.end - self.start + 1,
            );
            Some(
                HeaderValue::from_str(&format!(
                    "bytes={}-{}",
                    prev_start,
                    self.start - 1
                ))
                .expect("string provided by format!"),
            )
        }
    }
}

fn main() -> Result<()> {
    let url = "https://httpbin.org/range/102400?duration=2";
    const CHUNK_SIZE: u32 = 10240;

    let client = reqwest::blocking::Client::new();
    let response = client.head(url).send()?;
    let length = response
        .headers()
        .get(CONTENT_LENGTH)
        .ok_or(anyhow!("response doesn't include the content length"))?;
    let length = u64::from_str(length.to_str()?)
        .map_err(|_| anyhow!("invalid Content-Length header"))?;

    if !std::fs::exists("temp")? {
        std::fs::create_dir("temp")?;
    }
    let mut output_file = File::create("temp/download.bin")?;

    println!("starting download...");
    for range in PartialRangeIter::new(0, length - 1, CHUNK_SIZE)? {
        println!("range {:?}", range);
        let mut response = client.get(url).header(RANGE, range).send()?;

        let status = response.status();
        if !(status == StatusCode::OK || status == StatusCode::PARTIAL_CONTENT)
        {
            bail!("Unexpected server response: {}", status)
        }
        std::io::copy(&mut response, &mut output_file)?;
    }

    let content = response.text()?;
    std::io::copy(&mut content.as_bytes(), &mut output_file)?;

    println!("Finished with success!");
    Ok(())
}

Web serving

cat-web-programming::http-server

Serve data over HTTP.

Actix Web

RecipeCratesCategories
Create a web server with Actix Webactix-webcat-web-programming cat-web-programming::http-server

Axum

RecipeCratesCategories
Create a web server with axumaxumcat-web-programming cat-web-programming::http-server

Batteries-included frameworks

RecipeCratesCategories
locoloco_rscat-web-programming::http-server
Rust on NailsRust on Nailscat-web-programming::http-server

Cross-origin resource sharing

RecipeCratesCategories
Implement CORStower tower-httpcat-web-programming

GraphQL

RecipeCratesCategories
Create a GraphQL endpointasync-graphqlcat-web-programming::http-server

gRPC

RecipeCratesCategories
Implement gRPCtoniccat-web-programming::http-server

Working with hyper

RecipeCratesCategories
Implement an HTTP API with hyperhypercat-web-programming::http-server

Middleware

Other frameworks

Static website generators

Axum

RecipeCratesCategories
Create a web server with axumaxumcat-web-programming cat-web-programming::http-server

Create a web server with axum

axum axum-crates.io axum-github axum-lib.rs cat-asynchronous cat-network-programming cat-web-programming::http-server

axum is a web framework that focuses on ergonomics and modularity. It is an official part of the tokio project. axum is recommended for most new projects.

fn main() {
    todo!();
}

See also

Actix Web

RecipeCratesCategories
Create a web server with Actix Webactix-webcat-web-programming cat-web-programming::http-server

Create a web server with Actix Web

actix-web-website actix-web actix-web-crates.io actix-web-github actix-web-lib.rs cat-asynchronous cat-network-programming cat-web-programming::http-server cat-web-programming::websocket

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust. All Rust frameworks are fast, but choose actix if you need the absolutely maximum performance.

fn main() {
    todo!();
}

See also

Other Web Frameworks

Rust web framework comparison

Implement a HTTP server using rocket

rocket rocket-crates.io rocket-github rocket-lib.rs cat-web-programming::http-server

Web framework with a focus on usability, security, extensibility, and speed.

rocket-realworld-example

Rust + Rocket RealWorld framework implementation

fn main() {
    todo!();
}

leptos

leptos leptos-crates.io leptos-github leptos-lib.rs cat-web-programming::http-server

leptos⮳ is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces.

fn main() {
    todo!();
}

Static Website Generators

Create a simple website using a static website generator

zola zola-github zola-lib.rs cat-web-programming cat-web-programming::http-server

Zola⮳ is a fast static site generator in a single binary with everything built-in.

fn main() {
    todo!();
}

Themes for zola

AdiDoks is a modern documentation theme.

Middleware

tower

tower cat-web-programming cat-web-programming::http-server

tower⮳ is a library of modular and reusable components for building robust networking clients and servers.

Tower provides a simple core abstraction, the tower::Service⮳ trait, which represents an asynchronous function taking a request and returning either a response or an error. It can be used to model both clients and servers.

An additional abstraction, the tower::Layer⮳ trait, is used to compose middleware with Services. A tower::Layer⮳ is a function taking a Service of one type and returning a Service of a different type. The tower::ServiceBuilder⮳ type is used to add middleware to a service by composing it with multiple Layers. The tower::Layer⮳ trait can be used to write reusable components that can be applied to very different kinds of services; for example, it can be applied to services operating on different protocols, and to both the client and server side of a network transaction.

A number of third-party libraries support tower⮳ and the tower::Service⮳ trait: hyper⮳, tonic⮳ (gRPC).

tower-middleware-from-scratch

tower-http

tower_http cat-web-programming cat-web-programming::http-server

Tower HTTP⮳ contains HTTP specific Tower utilities.

use std::iter::once;
use std::sync::Arc;

use bytes::Bytes;
use http::Request;
use http::Response;
use http::header::AUTHORIZATION;
use http::header::CONTENT_TYPE;
use http::header::HeaderName;
use http_body_util::Full;
use tower::BoxError;
use tower::ServiceBuilder;
use tower_http::add_extension::AddExtensionLayer;
use tower_http::compression::CompressionLayer;
use tower_http::propagate_header::PropagateHeaderLayer;
use tower_http::sensitive_headers::SetSensitiveRequestHeadersLayer;
use tower_http::set_header::SetResponseHeaderLayer;
use tower_http::trace::TraceLayer;
// use tower_http::validate_request::ValidateRequestHeaderLayer;

// Our request handler. This is where we would implement the
// application logic for responding to HTTP requests...
async fn handler(
    _request: Request<Full<Bytes>>,
) -> Result<Response<Full<Bytes>>, BoxError> {
    let empty_body = Full::new(Bytes::new());
    let builder = Response::builder()
        .header("X-Custom-Foo", "bar")
        .status(http::status::StatusCode::OK);
    Ok(builder.body(empty_body).unwrap())
}

struct DatabaseConnectionPool;

impl DatabaseConnectionPool {
    fn new() -> Self {
        DatabaseConnectionPool
    }
}

// Shared state across all request handlers -
// in this case, a pool of database connections.
struct State {
    pool: DatabaseConnectionPool,
}

#[tokio::main]
async fn main() {
    // Construct the shared state.
    let state = State {
        pool: DatabaseConnectionPool::new(),
    };

    let content_length_from_response = 0;

    // Use tower's `ServiceBuilder` API to build a stack of tower
    // middleware wrapping our request handler.
    let _service = ServiceBuilder::new()
        // Mark the `Authorization` request header as sensitive
        // so it doesn't show in logs
        .layer(SetSensitiveRequestHeadersLayer::new(once(AUTHORIZATION)))
        // High level logging of requests and responses
        .layer(TraceLayer::new_for_http())
        // Share an `Arc<State>` with all requests
        .layer(AddExtensionLayer::new(Arc::new(state)))
        // Compress responses
        .layer(CompressionLayer::new())
        // Propagate `X-Request-Id`s from requests to responses
        .layer(PropagateHeaderLayer::new(HeaderName::from_static(
            "x-request-id",
        )))
        // If the response has a known size set the `Content-Length` header
        .layer(SetResponseHeaderLayer::overriding(
            CONTENT_TYPE,
            content_length_from_response,
        ))
        //// Authorize requests using a token
        //.layer(ValidateRequestHeaderLayer::bearer("passwordlol"))
        //// Accept only application/json, application/* and */*
        //// in a request's ACCEPT header
        //.layer(ValidateRequestHeaderLayer::accept("application/json"))
        // Wrap the `Service` in our middleware stack
        .service_fn(handler);
}

Investigate alternatives to tower

trillium trillium-crates.io trillium-github trillium-lib.rscat-web-programming::http-servercat-web-programming

A modular toolkit for building async web apps

Cross-origin resource sharing

RecipeCratesCategories
Implement CORStower tower-httpcat-web-programming

Implement CORS

tower tower-crates.io tower-github tower-lib.rs tower-http tower-http-crates.io tower-http-github tower-http-lib.rs cat-asynchronous cat-network-programming cat-web-programming

use std::convert::Infallible;

use anyhow::Result;
use bytes::Bytes;
use http::Method;
use http::Request;
use http::Response;
use http::header;
use http_body_util::Full;
use tower::Service;
use tower::ServiceBuilder;
use tower::ServiceExt;
use tower_http::cors::Any;
use tower_http::cors::CorsLayer;

async fn handle(
    _request: Request<Full<Bytes>>,
) -> Result<Response<Full<Bytes>>, Infallible> {
    Ok(Response::new(Full::default()))
}

#[tokio::main]
async fn main() -> Result<()> {
    let cors = CorsLayer::new()
        // Allow `GET` and `POST` when accessing the resource
        .allow_methods([Method::GET, Method::POST])
        // Allow requests from any origin
        .allow_origin(Any);

    let mut service = ServiceBuilder::new().layer(cors).service_fn(handle);

    let request = Request::builder()
        .header(header::ORIGIN, "https://example.com")
        .body(Full::default())
        .unwrap();

    let response = service.ready().await?.call(request).await?;
    println!("{:?}", response);
    assert_eq!(
        response
            .headers()
            .get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
            .unwrap(),
        "*",
    );
    Ok(())
}

See also

Batteries-included frameworks

RecipeCratesCategories
locoloco_rscat-web-programming::http-server
Rust on NailsRust on Nailscat-web-programming::http-server

loco

loco_rs loco_rs-crates.io loco_rs-github loco_rs-lib.rs cat-web-programming::http-server

fn main() {
    todo!();
}

Rust on Nails

Rust on Nails

See also

Building a SaaS with Rust and Next.js

GraphQL

RecipeCratesCategories
Create a GraphQL endpointasync-graphqlcat-web-programming::http-server

Create a GraphQL endpoint

async-graphql async-graphql-crates.io async-graphql-github async-graphql-lib.rs cat-asynchronous cat-network-programming

async-graphql is a high-performance graphql server library that's fully specification compliant. It integrates with actix-web, axum, poem, rocket, tide, and warp.

fn main() {
    todo!();
}

gRPC

RecipeCratesCategories
Implement gRPCtoniccat-web-programming::http-server

Implement gRPC

tonic tonic-crates.io tonic-github tonic-lib.rs cat-asynchronous cat-network-programming cat-web-programming

tonic implements gRPC over HTTP/2 with full support for asynchronous code. It works with tokio.

gRPC is an open-source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in the "last mile" of distributed computing to connect devices, mobile applications and browsers to backend services.

fn main() {
    todo!();
}

Hyper

RecipeCratesCategories
Implement an HTTP API with hyperhypercat-web-programming::http-server

Implement an HTTP API with hyper

hyper hyper-crates.io hyper-github hyper-lib.rs

hyper is a low-level HTTP implementation (both client and server). It implements HTTP/1 and HTTP/2. It works best with the tokio async runtime, but can support other runtimes.

fn main() {
    todo!();
}

Essential Rust links

Websites

Key websites

rust-lang.orgOfficial website for the Rust programming language
The Rust Programming Language"The Rust Book"
crates.ioThe official crate (library) registry
crates.io's categoriesList of categories on crates.io - search with category:<category-slug>
lib.rsCurated, unofficial alternative to crates.io
docs.rsThe central documentation website for Rust crates
blessedUnofficial guide to (the best of) the Rust ecosystem
github.comThe home of most Rust code repositories

Example code

Example codebases include:

  • A full template for a REST API written in Rust, engineered for maximum testability: rust-rest

RealWorld

realworld-example-apps

Source code for Rust books

Rust Cheatsheets

Cheatsheets

Comparison to other languages

Blogs, Podcasts, Meetups

Rust podcasts

Newsletters

Meetups

Rust Meetup Linz

Books

Companies that use or contribute to Rust

The following is an (incomplete) list of companies and organizations that use Rust. It combines information from the following sources, among others:

Please also consult the theirstack.com's list of companies using Rust.

1Password(github)All desktop and mobile apps share a single Rust codebase.
360DialogUses Rust for most of their service consumers.
49NordDevelops safe and secure industrial IoT hardware and applications using Rust.
9fin
ANIXEBuilds their travel services trading platform in Rust.
ARM
Accelerant
Actyx(github)Actyx is a peer-to-peer event stream database (a.k.a. reliable durable pub-sub) written completely in Rust.
Adacore(blog)
Afterpay
Airborne Engineering
Alchemy
AmazonAmazon Web Services has adopted Rust for certain infrastructure, networking, and machine learning services, including their serverless computing - see, for example, Amazon Firecracker⮳.
AppFlowy
AppSignalProvides monitoring products for web developers.
Apple
Aptos Labs
Astral(github)
AtlassianUses Rust to analyze petabytes of source code.
Attain
Automata
Autumn EngineeringOffers machine learning frameworks written in Rust.
Bencher(github)Continuous benchmarking tools designed to catch performance regressions in CI.
BitfuryExonum is an extensible framework for blockchain projects written in Rust.
Block
Brainport Digital Factory
Braintree PaymentsUses Rust for command-line utilities and batch processing as part of their third-party payment merchant services.
Braun Embedded(github)Provides firmware development services for ARM Cortex-M microcontrollers.
Brave(github)Uses Rust for its browser's ad-blocking engine.
Buoyant
CalyptechUsed for high performance embedded system components as an alternative to C.
CanonicalEverything from server monitoring to middleware!
CarGurus
Cash App
CephProvides an open-source storage system partially written in Rust. Rust bindings for librbd, an interface into the Ceph storage platform.
Chef(github)Offers a platform to configure, deploy, and manage applications, written in Rust.
Chroma(github)
Clever CloudAllow developers to host web applications. A part of their infrastructure is developed in Rust.
Cloudflare WorkersDeploy serverless code across the globe with Rust support.
Cloudflare(github)Uses Rust for its core edge logic. See, for example, Pingora (blog)⮳.
CodeDay
CoreOSCoreOS is s a open-sourced container host, using Rust as one of its main programming languages.
Corrode(github)Germany-based Rust consulting and development firm.
CourseraUses Rust for its learning platform.
CraftTheir core machine learning engine is written in Rust.
CryptapeTheir business intelligence platform built for blockchain is written in Rust.
CurrySoftware GmbH
DataRobot(github)Builds Machine Learning models that are implemented using Rust.
Deepgram(github)Speech AI Platform for transcription and understanding.
DelimiterRust powers their bare metal provisioning system.
DenoUses Rust for its JavaScript and TypeScript runtime.
Devolutions
Dioxus Labs(github)
DiscordUses Rust for both the client and server sides of its codebase, including their real-time communication services.
Disney
Distil NetworksUses Rust in their low-latency bot detection and mitigation platform.
DropboxUses Rust for its file-syncing engine. See, for example, Capture⮳.
DungeonFog(github)Uses Rust in their game engine and map-making tool for RPGs. New "Project Deios" map maker tool is written completely in Rust.
EVODevelops marketplaces for online shopping, fintech, and logistics. Development tools, containerization, monitoring and orchestration systems written in Rust.
Embark Studios
Embecosm
Espressif(github)Improving performance of embedded and IoT devices with using Rust in esp products.
ExonumSupports smart contracts written in Rust.
Faraday(github)Embeds AI in workflows.
FediFedi builds on Fedimint⮳, a module based system for building federated applications on top of Bitcoin.
Fermyon
Ferrous Systems(github)Ferrous is a Rust consulting firm specializing in embedded systems development.
FigmaUses Rust for parts of its design and collaboration tools. Their real-time multiplayer syncing server (used to edit all Figma documents) is written in Rust.
Firo Solutions(github)Firo Solutions is a notification service that notifies the end user about security vulnerabilities. Parse large amounts of data using Rust.
Flowdesk
Fly(github)Globally distributed reverse-proxy and app hosting.
Freiheit
GRAIL
Gama Space
Gitoxide Labs (github)
GoogleUses Rust for ChromeOS, Google Fuchsia, and other projects. In Android 13, about 21% of all new native code (C/C++/Rust) is in Rust. There are approximately 1.5 million total lines of Rust code in Android across new functionality and components such as Keystore2, the new Ultra-wideband (UWB) stack, DNS-over-HTTP3, Android’s Virtualization framework (AVF), and various other components and their open source dependencies.
Grafbase
Greenbone
GremlinSafely and efficiently causing controlled chaos.
HighTec EDV Systeme
HiveOffers pre-trained AI models for automating content moderation. Hive 2.0 is written in Rust.
Hove(github)Navitia API provides ways to query public transport data, including a multi-criteria journey engine.
HuaweiDevelops telecommunication equipment and consumer electronics, in part via Rust.
HuggingFace(github)Many components within the Hugging Face ecosystem for AI, including safetensors, tokenizer, and candle, are implemented using Rust.
Hyperswitch (Juspay.io)(github)Their open-source payment switch that allows businesses to interact with multiple payment gateways is built with Rust.
Immunant(github)Immunant specializes in translation from C to Rust and exposing legacy C/C++ through safe Rust interfaces.
Infinyon
Integer32(github)Integer 32 is a consultancy that delivers high-quality Rust code.
JFrog
Jetbrains
KDAB
Keyrock
Knoldus
Kong
KrakenCryptocurrency exchange, based in the USA.
Lechev.space(github)Building various aerospace-related projects including a Ground station service using Rust.
Leptos(github)
LinebenderSee, for example, Vello
Linkerd
Lynx
Machina Labs, Inc
MaidsafeBuilds a decentralized data and communication network.
Mainmatter
Maplibre(github)
Materialize(github)Uses Rust in their streaming data warehouse. An engine for incrementally maintaining database views. Materialize core is a single binary written in Rust.
Meilisearch(github)
Memfault
MetaUses Rust for the source control backend, Libra (now Diem), and other server-side projects. Facebook's primary source control system is partially written in Rust.
MicrosoftUses Rust for various projects, including the Windows kernel, Azure, and Visual Studio. windows-rs allows you to call Windows API using Rust. There are several Azure services also using Rust, including Azure IoT Edge⮳ and Kusto, the core query and storage engine for Azure Data Explorer⮳.
MoveParallel
Mozilla(github)The creators of Rust, Mozilla has used the language in various projects, including parts of the Firefox web browser. See Servo⮳.
Multi media, LLC
NextRoll
Novo Nordisk
Npm(github)The package manager for JavaScript has components written in Rust. Also use Rust for their authorization service.
NvimUses Rust for its powerful text editor.
OVHcloudUse Rust to build a high performance, highly available log management system.
OneSignal(blog)High volume, cross platform push notification delivery.
OpenUK
Open Source Security
OpenSource Science (OS-Sci)
OxidOS Automotive
PUCPR
Parity Technology(github)Creates open-sourced networks, consensus protocols, and cryptography, entirely in Rust.
Personio
Pollen Robotics
PolySyncBuilds safety-critical runtime environments & infrastructure for autonomous vehicles.
Postmates
Qdrant(github)High-performance, massive-scale Vector Database and Vector Search Engine for the next generation of AI
QumuloOffers a hybrid data storage for large file and object data.
Qwiet
Readyset
Red Badger
Red HatEngages with Rust in several projects, particularly in areas related to system programming and container technology.
Red Iron(github)Red Iron is the Rust division of OCamlPro, a French consultancy specializing in programming languages, formal methods and high reliability software.
RedJack
Red Sift(github)Container monitoring with eBPF.
RenaultFrench automotive manufacturer.
RoutificHigh performance route optimization software.
RustNL
Rustdesk(github)
Salesforce Uses Rust & WASM for MuleSoft's Flex Gateway, a new gateway running on Envoy.
SandboxVR
SandstormThe backend of their Collections app is written in Rust.
Scythe Robotics
Sensirion
SentryUses Rust for its performance-critical components. JavaScript, Java and iOS event processing and the command-line client for the Sentry API.
Shopify
Slint(github)
SlowtecTheir automation systems are entirely written in Rust.
SmartThings (Samsung)Memory-safe embedded applications on our SmartThings Hub and supporting services in the cloud.
Snips (Sonos)AI assistants that are private by design.
Solana LabsUse Rust as their main programming language for smart contracts.
SpectralOps
Sprinklenet
Spruceid
Square
Stackable(github)Open-source software development company that handles data. Creating a modular open source data platform. Rust powers all our operators to help make popular data applications straightforward to run on Kubernetes.
StarlabProvides embedded security.
StarryRe-imagining broadband by engineering a new wireless access network.
Sui
Svix(github)Enterprise-grade webhooks service.
System76(github)Manufacturer of Linux computers and keyboards. As a Linux-based computer-manufacture, much of their infrastructure and desktop Linux projects are written in Rust.
Tag1 Consulting
Tailcall(github)A cloud native solution to streamline API management across edge, middle, and service layers.
Tarro
Techfund
Terminal Technologies
Tesla
Threema
Tikv(github)
TockOS(github)
Trace Machina
Tracel.ai(github)
TreeScaleTreeScale implements distributed PubSub system using Rust and MIO
Tremor(github)
Tsy Capital
Tweede Golf(github)Create safe internet infrastructure with Rust and secure connected devices with Embedded Rust.
Vail Systems
Veecle
VersionEye(github)Use Rust to implement a command line tool which can identify software dependencies by their SHA values.
Volvo
Warp
Watchful
WildfishFast processing and importing of cryptocurrency market data.
WyeWorks
Wyliodrin
Xfusion
Yelp
Yomura FiberRust powers their GPON provisioning and statistic gathering.
ZS
Zama(github)Open source cryptographic tools that make protecting privacy easy.
Zeplin

Learn Rust

Call for contributions

This book is in its early days. Contributions, from small edits to whole chapters, are most welcome.

It is also intended to be easy for (new) Rust programmers to contribute to and get involved with the Rust community.

Feel free to submit an issue or a pull request to the repo⮳. Draft pages are kept in this folder⮳. An informal (and very long) list of subjects we would like to cover is kept in the topics of interest⮳ page.

Embedded examples should be ideally runnable on the Rust playground⮳ or at least directly copy-pasteable into Rust code.

This book's long-term goal is the coverage of the 'most commonly used' Rust crates, as defined by blessed.rs⮳, the most downloaded libraries in crates.io⮳, and 'high quality crates' per lib.rsstatistics⮳. Review key crates for topic ideas.

Unless you explicitly state otherwise, any contribution you intentionally submit for inclusion in this book shall be licensed under the same terms than the rest of the book, without any additional restrictions or conditions.

Please read CONTRIBUTING.md for more details.

See also

rust-howto-todo-github

rust-howto-contributing

rust-howto-drafts

rust-howto-github

Topics of interest

The following are topics that deserve (additional) coverage and examples:

  • Rust GUI
  • Rust patterns
  • Rust Macros
  • Advanced data structures
  • Testing
  • GPU processing, CUDA
  • Machine learning, Tensorflow
  • Email
  • Template Engines
  • Caching
  • Compilers
  • WASM
  • Algorithms
  • Rust patterns
  • Authentication / authorization: OAuth2, LDAP/AD, DNS lookups...
  • Continuous Deployment & Integration (CD / CI) for Rust projects
  • Use of Rust in AWS and other Cloud services
  • Serverless Rust
  • More database examples, including object databases, graph databases, e.g. BonsaiDB, neo4j
  • Embedded
  • Visualization
  • Graphics
  • Games, Game Engines
  • Search engines
  • Compression: Zip files and other archives
  • Buffer pools, garbage collection, or other reference-counted examples
  • IPv6 address processing
  • Cloud: load balancers, status reporting (Vigil), routing, orchestration, containers
  • Reverse proxies
  • Web programming
  • Virtualization
  • Version control: libgit2: clone, change branches, create commits, push, pull
  • Crypto, SSL, SSH, other public key encryption, X.509, RusTLS
  • Network programming: Basic and advanced TCP/IP networking
  • Interfacing with FLTK (Fast Light Tool Kit)
  • Raft Consensus library
  • Network file systems
  • Statistics, math
  • Sound
  • API bindings
  • FFI
  • Hardware support
  • Build Utils
  • Rendering
  • High-performance computing: OpenMP, etc.
  • Social media APIs
  • Personal file sharing: OwnCloud, etc.
  • Emulators
  • Accessibility
  • Internationalization
  • Localization
  • Multimedia
  • Computer Vision
  • Robotics
  • Simulation
  • Science (Geo, Neuro)
  • Finance
  • Aerospace-related crates: drones, UAVs, space protocols, simulation...
  • Cryptocurrencies

Please also consult the TODO.md file and the drafts folder.

Repo structure

  • The repo contains a book, which markdown sources are in the src folder.
  • After the book is built using mdbook⮳, the resulting HTML and Javascript are found in book/html.
  • The intermediate (processed) Markdown is in book/markdown. The mdbook⮳ configuration is in book.toml; the templates and assets are in theme and static respectively.
  • The Rust code is organized as a cargo⮳ workspace:
    • Examples that are embedded in the book are found in crates below crates/ex, named after sections of the book or grouping multiple (crates.io) categories of examples. Each example is in a single, short .rs file. The Cargo.toml within these crates list the dependencies used by the embedded examples. Use cargo add <crate> -F <feature> while in the appropriate crate folder to add more as required.
    • Additional examples that are too long or complex to be inserted in the book itself can be added under crates/xmpl.
    • crates/tools contains utilities to build sections of the book, for example some indices.
  • The Dev Container and Docker (Compose) configuration files are found in .devcontainer.

Development Environment Setup

Using VS Code

Clone the repo⮳ and open the folder in VS Code⮳. Edit .devcontainer/.env if needed. VS Code should prompt you to open the code in a docker⮳ container, which installs mdbook⮳ and rust tooling automatically. Make sure you have previously installed

Note that opening the code folder in VS Code may take a little while the first time around.

Other

If you are not using VS Code, install the Dev Container CLI⮳ or simply install the required tools on your local machine:

sudo apt-get update # or equivalent for other distros
# sudo apt-get install fzf # optional
# sudo apt-get mold clang # if using
rustup update
rustup component add clippy
cargo install cargo-nextest
cargo install mdbook
cargo install just
cargo install mdbook_linkcheck
cargo install mdbook-utils
# for cargo +nightly fmt
rustup toolchain install nightly
rustup component add rustfmt --toolchain nightly

You may need sudo apt-get install libsqlite3-dev on WSL.

Review .devcontainer/Dockerfile for other dependencies.

Book Editing and Example Code Development

Type just⮳ (a tool similar to make⮳) in your favorite shell to lists all commonly used recipes during book editing and example code development.

Use just serve to preview the book by serving it locally on http://localhost:3000⮳.

To add or edit the book, simply update or add a .md file in the appropriate src⮳ subfolder, then add a link in SUMMARY.md⮳.

  • Add Rust code examples under crates/ex/<crate_name>/tests/<folder e.g. category>⮳.
    • Make sure to format your code (just fmtall or cargo +nightly fmt --all), check it compiles (just buildall or cargo build --all-targets), lint it (just clippyall or cargo clippy --all-targets), and test it (just testall or cargo test --test <name> for an individual example). You may also cargo run --example <name>.
    • Include your code example in the Markdown via {{# include /path/to/file.rs}} within pairs of triple backticks.
  • You may write very short examples directly in the Markdown (but they won't be be formatted / linted automatically).
  • rust language code blocks in the Markdown will automatically get a play button, which will execute the code in the Rust Playground⮳ and display the output just below the code block. Adding the mdbook-runnable attribute forces the play button to be displayed when ignore⮳ is set.
  • The Rust playground only supports the top 100 most downloaded libraries and libraries used by the Rust Cookbook. noplayground⮳ removes the play button if a code block does not work on the playground.
  • Example projects that are too complex to be inserted in the book itself (e.g. that include multiple modules) should be added as separate folders below crates/xmpl. Use cargo new or cargo init to create packages as usual. Insert a link to the appropriate GitHub page in the markdown.

Verify the markdown is properly rendered using just serve or mdbook serve --open. Pushing a commit to the main branch on GitHub will trigger a GitHub Action workflow that checks formatting / linting, builds / tests all examples, then deploys the book to GitHub Pages.

Dev Container and Docker

The development target of the multi-stage .devcontainer\Dockerfile is used by .devcontainer/devcontainer.json to install mdbook⮳ and rust tooling.

If you don't want to use Dev Container, use the following from the project's root directory to manually build the docker⮳ image and run it.

docker build --file .devcontainer/Dockerfile --target development --tag rust_howto_dev --build-arg RUST_IMAGE_LABEL=1.75.0-slim-bookworm --build-arg MDBOOK_VERSION=0.4.36 .
docker run --rm --detach --name rust_howto_dev1 --volume $(pwd):/code rust_howto_dev
docker exec -it rust_howto_dev1 bash

To cache the crate and the target folders from run to run, add

--mount type=volume,src=rust_howto_cargo_crate_cache,dst=/usr/local/cargo/registry/
--mount type=volume,src=rust_howto_cargo_target_cache,dst=/cargo-target-rust_howto/

To connect to the (host OS) docker engine from within the container, add

--mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker-host.sock

Docker Compose

Test the docker compose setup used during developement (which Dev Container runs) with:

cd ./.devcontainer
docker compose build # uses compose.yaml and compose.override.yaml
docker compose up -d
# or simply
docker compose up --build -d

Deployment to GitHub Pages

The continuous integration worflow is found under .github.

Test the docker compose setup used during CI using:

cd ./.devcontainer
docker compose -f compose.yaml -f compose-ci.yaml build
docker compose -f compose.yaml -f compose-ci.yaml run book # or simply docker compose -f compose.yaml -f compose-ci.yaml up

It uses the ci target in .devcontainer/Dockerfile.

To test the docker⮳ image manually, use

docker build --file .devcontainer/Dockerfile --target ci --tag rust_howto_ci --build-arg RUST_IMAGE_LABEL=1.75.0-slim-bookworm --build-arg MDBOOK_VERSION=0.4.36 .
docker run -it --rm --name rust_howto_ci1 --volume $(pwd)/book:/code/book rust_howto_ci bash

Related Stackoverflow question

Push image to Docker Hub

From the project root folder, use the following to build and push the development image:

docker build --file .devcontainer/Dockerfile --target development --tag johncd/rust_howto_dev:latest --build-arg RUST_IMAGE_LABEL=1.75.0-slim-bookworm --build-arg MDBOOK_VERSION=0.4.36 .
# or docker tag rust_howto_dev johncd/rust_howto_dev:latest
docker login
# or docker login -u "user" -p "password" docker.io
docker push johncd/rust_howto_dev:latest

Use the following to build and push the CI image:

docker build --file .devcontainer/Dockerfile --target ci --tag johncd/rust_howto_ci --build-arg RUST_IMAGE_LABEL=1.75.0-slim-bookworm --build-arg MDBOOK_VERSION=0.4.36 .
docker login
docker push johncd/rust_howto_ci:latest

Optional pre-processors

  • mdbook-linkcheck⮳ is a backend for mdbook⮳ that will check links. Install with cargo install mdbook-linkcheck. Uncomment the related section in book.toml.

Generate the docs.rs Documentation

Use just doc to generate the documentation for docs.rs.

cargo doc --open does not seem to work when running from a Dev Container in VS Code; the script that opens URLs into an external browser (see $ echo $BROWSER) does not handle raw HTML. Use python3 -m http.server 9000 or live server to serve the files instead. See the doc recipe in justfile⮳.

Using a Dev Container feature

Alternatively, use the "Desktop lite" Dev Container feature⮳ to install a light GUI manager. Add the following to devcontainer.json:

"features": {
    "ghcr.io/devcontainers/features/desktop-lite:1": {}
},
"forwardPorts": [
    6080
],
"portsAttributes": {
    "6080": {
        "label": "desktop"
    }
},

and the following to the Dockerfile

RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get install -y firefox-esr

Optionally apt-get install xdg-utils to check that Firefox is the default for text/html:

xdg-mime query default text/html
# or for more details:
XDG_UTILS_DEBUG_LEVEL=2 xdg-mime query default text/html

xdg-settings --list
xdg-settings get default-web-browser

Point your browser to http://localhost:6080 and use vscode as the password. Open the HTML file of your choice with:

xdg-open /cargo-target-rust_howto/target/doc/deps/index.html

Other methods to preview the documentation HTML

  • Add the target directory e.g. /cargo-target-rust_howto/target to the VS Code Explorer view (File > Add Folder to Workspace...), then right-click the /cargo-target-rust_howto/target/doc folder in the VS Code Explorer view and select Download... or use VS Code's built-in Simple Browser command.
  • Or install the Live Server or MS Live Preview VS Code extensions.

Publish to crates.io

The crates/publish folder contains a placeholder crate, so that the book could be located when searching on crates.io.

  • cargo update if necessary
  • Go to crates.io, sign in, and create an API token in Account Settings > API Tokens.
  • Use cargo login to save the token in $CARGO_HOME/credentials.toml.
  • cd crates/publish
  • cargo build --locked --release
  • cargo clippy
  • cargo run --release
  • cargo doc
  • Review cargo package --list
  • cargo package
  • Review the packaging output in /cargo-target-rust_howto/target/package.
  • When ready, cargo publish --dry-run; cargo publish

Index

*, 1
(), 1
.gitignore, 1
Accessibility, 1
Ace, 1
Actix, 1
actix-web, 1, 2, 3, 4, 5
Aead, 1, 2
Aes, 1, 2
Aes-gcm, 1
aes-gcm, 1, 2
aes-gcm-siv, 1, 2
Algorithms, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22
Allocation, 1
Ansi, 1, 2, 3, 4, 5
ANSI terminals, 1
Ansi_term, 1
ansi_term, 1, 2, 3
ansi_term::ANSIString, 1, 2
ansi_term::Color, 1
ansi_term::Style, 1, 2, 3
ansi_term::Style::new, 1
ansi_term:Style, 1, 2
ansiterm, 1
anstream, 1
anstyle, 1
anyhow, 1, 2, 3, 4
anyhow::Result, 1
API bindings, 1, 2, 3
apk, 1
App_dirs, 1, 2
application/x-www-form-urlencoded, 1
approx, 1, 2, 3
approx::assert_abs_diff_eq, 1
Arc, 1, 2
arc-swap, 1
Argument parsers, 1
Arguments, 1
arrayvec, 1
Asn1, 1
asref, 1
Assertions, 1
Async, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
async, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
Async channels, 1, 2
async fn, 1
async-channel, 1, 2, 3
async-graphql, 1
async-std, 1, 2, 3, 4, 5, 6
async-stream, 1, 2
async-trait, 1, 2, 3, 4
async/await, 1
Asynchronous, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36
Asynchronous programming, 1
Asynchronous runtime, 1
Atomic, 1, 2, 3, 4, 5
Atomic types, 1
Atomics, 1
Attributes, 1, 2, 3
Authentication, 1
Autotraits, 1
await, 1, 2, 3, 4
axum, 1, 2, 3, 4
bacon, 1, 2, 3, 4
Badges, 1
Base URL, 1
Base32, 1, 2
Base64, 1, 2
base64, 1, 2, 3
base64::decode, 1
base64::encode, 1
Basedir, 1, 2
Bash, 1
bat, 1, 2, 3
bevy, 1, 2, 3, 4
Big-endian, 1
Bignum, 1, 2, 3
Binary, 1, 2
Binary crate, 1
bincode, 1
Bit, 1
bitfield, 1
bitflags, 1, 2, 3
bitflags::bitflags, 1
Bitmask, 1
Bitwise operations, 1
blessed.rs, 1, 2, 3
Blocking code, 1
Blocking operation, 1
Blogs, 1
bon, 1
Book, 1, 2
Books, 1
bool, 1
Borrowing, 1, 2
bottlerocket, 1, 2
Boundary, 1
Box, 1
broot, 1, 2
Build metadata, 1
Build Utils, 1, 2
Build utils, 1, 2
build.rs, 1
Builder, 1, 2, 3
Byte, 1
Byte order, 1
bytemuck, 1
byteorder, 1, 2, 3, 4
bytes, 1, 2
Caching, 1, 2
Calendar, 1
Capture of variables, 1
Cargo, 1, 2, 3, 4, 5, 6
cargo, 1, 2, 3, 4
Cargo plugins, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
cargo plugins, 1
cargo-audit, 1
cargo-auditable, 1, 2, 3, 4
cargo-binstall, 1
cargo-cache, 1, 2, 3, 4, 5
cargo-crates, 1, 2, 3
cargo-deny, 1
cargo-edit, 1, 2, 3
cargo-expand, 1, 2, 3, 4, 5
cargo-generate, 1, 2
cargo-hack, 1, 2, 3, 4
cargo-hakari, 1, 2, 3
cargo-husky, 1, 2, 3
cargo-license, 1
cargo-limit, 1
cargo-machete, 1
cargo-make, 1, 2, 3, 4, 5, 6
cargo-nextest, 1, 2
cargo-outdated, 1
Cargo-subcommand, 1
cargo-tarpaulin, 1
cargo-udeps, 1, 2, 3
cargo-watch, 1
cargo-wizard, 1, 2, 3
cargo-xtask, 1, 2
cargo-zigbuild, 1
Cargo.lock, 1, 2
Cargo.toml, 1
Cbor, 1
cc, 1, 2, 3, 4, 5, 6
cc::Build, 1, 2
cc::Build::compile, 1
cc::Build::cpp, 1
cc::Build::define, 1
cc::Build::flag, 1
cc::Build::include, 1
Ccache, 1
cdylib, 1
chacha20poly1305, 1
Chan, 1
Channel, 1, 2, 3
Channels, 1, 2, 3, 4, 5
Cheat sheets, 1
chrono, 1, 2, 3, 4, 5, 6, 7, 8
chrono::Date::checked_add_signed, 1
chrono::Date::checked_sub_signed, 1
chrono::Datelike, 1
chrono::DateTime, 1, 2, 3
chrono::DateTime::format, 1, 2, 3
chrono::DateTime::from_utc, 1
chrono::DateTime::parse_from_rfc2822, 1, 2
chrono::DateTime::parse_from_str, 1, 2, 3
chrono::DateTime::to_rfc2822, 1
chrono::DateTime::to_rfc3339, 1
chrono::format::strftime, 1, 2, 3
chrono::naive::NaiveDate, 1
chrono::naive::NaiveDate::from_ymd, 1
chrono::naive::NaiveDateTime, 1
chrono::naive::NaiveDateTime::from_timestamp, 1
chrono::naive::NaiveDateTime::timestamp, 1
chrono::naive::NaiveTime, 1
chrono::naive::NaiveTime::from_hms, 1
chrono::offset::FixedOffset, 1
chrono::offset::Local::now, 1, 2
chrono::offset::Utc::now, 1
chrono::Timelike, 1
ciborium, 1
Clap, 1
clap, 1, 2, 3, 4, 5, 6
clap::Arg::long, 1
clap::Arg::short, 1
Cli, 1, 2, 3, 4
Client, 1
clippy, 1
Clone-on-write, 1
Closures, 1
Cmd, 1
Code example, 1
Coercion, 1
Color, 1, 2, 3, 4, 5, 6
color-eyre, 1
Colors, 1
Command line utilities, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
Command runner, 1
Command-line, 1
Command-line interface, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
Comparison to other languages, 1
Compression, 1, 2, 3, 4
concat-string, 1
concat_strs, 1, 2
Concatenation, 1
Concurrency, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45
Concurrent, 1, 2
Concurrent associative array, 1
Concurrent contexts, 1
Condvar, 1
Config, 1
config, 1, 2, 3, 4
Configuration, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
Configuration management, 1
confy, 1
Console, 1
console, 1, 2
const, 1
Constant-time, 1
Constructor, 1
Consumer, 1, 2
Content length, 1
Content type, 1
Contents, 1
Continuous integration worflow, 1
Control flow, 1
Cooperative multitasking, 1
Cores, 1
cornucopia, 1, 2, 3
cos, 1
cosmic-text, 1
Cow, 1
CPU, 1
Cpu, 1
CPU bound, 1
CPU cores, 1
CPU-heavy computations, 1
Cpus, 1
Crate, 1, 2
Crate root file, 1
Crates, 1
crates.io, 1, 2, 3, 4, 5
crates_io_api, 1, 2
Cross-interpretation, 1
crossbeam, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
crossbeam spawn, 1
crossbeam-channel, 1, 2, 3, 4, 5, 6, 7
crossbeam-queue, 1, 2, 3, 4
crossbeam-utils, 1, 2, 3, 4
crossbeam::atomic::AtomicCell, 1
crossbeam::scope, 1, 2, 3, 4
crossbeam::thread::Scope::spawn, 1, 2
crossbeam_channel, 1
crossbeam_channel::bounded, 1
crossbeam_channel::Receiver::iter, 1
crossbeam_channel::Sender::send, 1
crossterm, 1
Crypto, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
Cryptography, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
csv, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
csv::ByteRecord, 1
csv::invalid_option, 1
csv::Reader::deserialize, 1, 2
csv::ReaderBuilder::delimiter, 1
csv::StringRecord, 1
csv::Writer, 1
csv::Writer::flush, 1
csv::Writer::serialize, 1
csv::Writer::write_record, 1
Current time, 1
Current working directory, 1
Curve25519, 1, 2
Custom deserializer, 1
Custom event formatter, 1
Custom logger, 1
Custom logger configuration, 1
Dangling references, 1
Dashboard, 1
dashmap, 1, 2, 3, 4, 5, 6
Data encoding, 1
Data parallelism, 1
Data races, 1
Data structures, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
data-encoding, 1, 2, 3, 4, 5
Database, 1, 2, 3
Database implementations, 1
Database interfaces, 1, 2, 3, 4
Database management systems, 1
Databases, 1, 2, 3, 4, 5, 6, 7, 8
datafusion, 1
Date, 1, 2
date, 1
Date and time, 1, 2, 3, 4, 5, 6, 7
DateTime, 1
deadpool, 1
Debug message, 1
Debug output, 1
Debugging, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
Debugging information, 1
Decimal, 1
Decode, 1
Decoding, 1
Dedicated thread, 1, 2, 3
Default implementation, 1
Delimiter, 1
Dependencies, 1, 2
der, 1, 2
Dereference operator, 1
Derivable traits, 1
Derive, 1, 2, 3, 4
derive, 1, 2
derive_builder, 1
derive_more, 1, 2, 3, 4, 5, 6
Deserialization, 1
Deserialize, 1
desktop-lite, 1
Destructuring, 1
Detect loops for a given path, 1
Dev Container, 1, 2
Dev Containers, 1
Development, 1
Development tools, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33
Development tools::Build Utils, 1
Development tools::Debugging, 1
devx, 1
devx-cmd, 1
devx-pre-commit, 1
diesel, 1, 2
Digest, 1, 2, 3
digest, 1, 2, 3
digest::Context, 1
digest::Digest, 1
directories, 1
Directory, 1, 2, 3, 4
dirs, 1
Distributions, 1
Diverging functions, 1
Django, 1
docker, 1, 2, 3
docker compose, 1
Dockerfile, 1
Docopt, 1
docs.rs, 1, 2, 3
Document, 1
Documentation, 1
Documentation comments, 1
Dotenv, 1
dotenv, 1, 2, 3
dotenvy, 1, 2, 3
Doxygen, 1
druid, 1, 2
dsa, 1, 2
duct, 1, 2, 3
Duplicate filenames, 1
Duration, 1
dyn, 1
dyn-clone, 1, 2, 3, 4
Ecc, 1, 2, 3, 4, 5, 6
ecdsa, 1, 2
Ed25519, 1
ed25519, 1, 2
ed25519-dalek, 1
egui, 1, 2, 3
Elapsed time, 1
elasticsearch, 1, 2, 3, 4
Email address, 1
embassy, 1, 2, 3
Embedded development, 1, 2, 3
Encode, 1
Encoding, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39
Encryption, 1, 2, 3
Endian, 1
Enums, 1, 2, 3
Env, 1, 2, 3
env_logger, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
env_logger::Builder, 1, 2
env_logger::Builder::format, 1
env_logger::Builder::init, 1
env_logger::Builder::parse, 1
env_logger::Builder::target, 1
env_logger::fmt::Target, 1
env_logger::init, 1
Environment, 1, 2
Environment variables, 1, 2
envy, 1, 2, 3
Equal, 1
Error, 1, 2
Error handling, 1
Error-handling, 1, 2
error_chain, 1
Events, 1
exa, 1, 2, 3
Example code, 1
Executables, 1
External command, 1, 2
External FFI bindings, 1
eyre, 1, 2, 3
F32, 1
f32, 1
F64, 1
f64, 1
fancy-regex, 1
Fast, 1
fastrand, 1
Feature-unification, 1
FFI, 1
Fields, 1
File, 1, 2
File sizes, 1
File systems, 1
Files, 1
Filesystem, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18
Finance, 1
Financial, 1
Firefox, 1
Fixed, 1
Flag, 1
Flags, 1
flagset, 1, 2
flate2, 1, 2, 3, 4, 5, 6
flate2::read::GzDecoder, 1
flate2::write::GzEncoder, 1, 2
floem, 1
flume, 1
flurry, 1
fn, 1
Folder, 1, 2
form-urlencoded, 1
form_urlencoded::byte_serialize, 1
form_urlencoded::parse, 1
Formal methods, 1
Format, 1
Formatter, 1
Framework, 1, 2, 3
FromStr, 1
Function, 1
Functions, 1
Future, 1, 2, 3, 4
Futures, 1, 2, 3, 4, 5, 6, 7, 8, 9
futures, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
futures::future::Select, 1
futures::prelude::stream::StreamExt::filter, 1
futures::prelude::stream::StreamExt::fold, 1
futures::prelude::stream::StreamExt::for_each_concurrent, 1
futures::prelude::stream::StreamExt::map, 1
futures::prelude::stream::TryStreamExt::try_filter, 1
futures::prelude::stream::TryStreamExt::try_fold, 1
futures::prelude::stream::TryStreamExt::try_for_each_concurrent, 1
futures::stream::Stream, 1
futures_executor, 1, 2, 3, 4
futures_executor::block_on, 1
Game development, 1
Game engines, 1
Garbage, 1, 2, 3
Garbage collection, 1
Garbage collector, 1
Gcm, 1
Generator, 1
Generics, 1, 2
Ghash, 1
Git, 1, 2
Gitbook, 1
Github actions, 1
GitHub API, 1
GitHub API - Rate limiting, 1
Gitignore, 1
glidesort, 1, 2
Glob, 1, 2
glob, 1, 2, 3, 4, 5, 6
glob::glob_with, 1
glob::glob_with::glob_with, 1
glob::MatchOptions, 1
Global mutable state, 1
globset, 1
Gmp, 1
Golang, 1, 2, 3
gping, 1, 2
Grapheme, 1
Graphemes, 1
Graphics, 1
gRPC, 1
GUI, 1, 2
GUI manager, 1
Guppy, 1
Hardware, 1
Hardware support, 1, 2
Hash, 1, 2, 3
Hashing, 1, 2
Hashmap, 1, 2, 3
Hashtags, 1
Header, 1
Heap, 1
Heap allocations, 1
helix, 1
Hex, 1, 2
Hexadecimal representation, 1
Hierarchical, 1
Hook, 1
Hour/minute/second, 1
href, 1
Html, 1, 2
HTML document, 1
Http, 1, 2, 3, 4
http, 1, 2, 3, 4
HTTP authentication, 1
HTTP client, 1, 2, 3, 4, 5, 6, 7, 8, 9
HTTP GET request, 1
HTTP response, 1
HTTP server, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
http-body-util, 1
hyper, 1, 2, 3, 4, 5, 6, 7, 8, 9
hyper::header!, 1, 2
hyper::header::AUTHORIZATION, 1
hyper::header::USER_AGENT, 1
i128, 1
i16, 1
i32, 1
i64, 1
i8, 1
iced, 1, 2
if let, 1
ignore, 1, 2, 3
image, 1, 2, 3, 4, 5, 6
image::DynamicImage::resize, 1
image::ImageBuffer::new, 1
image::ImageBuffer::put_pixel, 1
image::ImageBuffer::save, 1
image::Rgb::from_channels, 1
Images, 1
Immutable type, 1
indexmap, 1
indicatif, 1, 2, 3, 4
infisearch, 1, 2, 3
Inner value, 1
Inode, 1
inquire, 1
Interfaces, 1
Interior mutability, 1
Intermediate representation, 1
Internationalization (i18n), 1
Interpreters, 1
Invalid data, 1
Io, 1, 2, 3, 4, 5
isize, 1, 2
ISO 8601, 1
Iterator, 1, 2, 3, 4
Iterators, 1
iterators, 1
itertools, 1, 2, 3, 4, 5
Itu, 1
Jinja2, 1
Join, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
join!, 1
JPEG, 1
JSON, 1
just, 1, 2, 3, 4, 5, 6, 7, 8
kanal, 1, 2, 3, 4, 5
kani, 1, 2, 3, 4
Key, 1, 2
Key crates, 1
Keys, 1
L1 norm, 1, 2, 3
L2 norm, 1, 2, 3
Lazy, 1
Lazy static, 1
lazy_static, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
len, 1
lens, 1
lens_rs, 1, 2
leptos, 1, 2, 3
lexopt, 1
lib.rs, 1, 2, 3, 4, 5
libc, 1
Library crate, 1
Libsodium, 1
Lifetimes, 1, 2
Line editing, 1
linfa, 1, 2, 3, 4
Links, 1
Lint checks, 1
Little-endian, 1
Lock-free, 1, 2, 3, 4
loco_rs, 1, 2
Log, 1, 2, 3
log, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
Log configuration, 1
Log levels, 1, 2
Log output to a custom location, 1
Log verbosity, 1
Log4, 1
log4rs, 1, 2, 3, 4, 5
log4rs::append::file::FileAppender, 1
log4rs::config::Config, 1
log4rs::encode::pattern, 1
log::debug, 1
log::error, 1
log::Level, 1
log::LevelFilter, 1
log::Log, 1, 2
log::Record::args, 1
log::Record::level, 1
Logger, 1, 2
Logging, 1, 2, 3, 4, 5
Logging utilities, 1
Logical cpu cores, 1
lru, 1, 2, 3
lsd, 1, 2, 3
Macro, 1, 2, 3
Macros, 1, 2, 3
main, 1, 2
make, 1
Map, 1
mapv, 1
Markdown, 1, 2
Markup, 1
match, 1, 2
Math, 1
Mathematics, 1, 2, 3, 4, 5, 6, 7, 8
md-5, 1, 2
Md5, 1
Mdbook, 1, 2, 3, 4, 5
mdbook, 1, 2, 3, 4, 5, 6, 7
mdbook-cmdrun, 1, 2
mdbook-hide, 1, 2, 3
mdbook-journal, 1, 2, 3, 4
mdbook-keeper, 1, 2
mdbook-linkcheck, 1, 2, 3
mdbook-pagetoc, 1, 2
mdbook-private, 1, 2
mdbook-tera, 1, 2, 3, 4
mdbook-theme, 1, 2
mdbook-toc, 1, 2
mdbook-yapp, 1
Meetups, 1
meilisearch, 1
memmap2, 1, 2, 3
memmap2::Mmap::map, 1
Memory, 1
Memory leaks, 1
Memory management, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
Memory map, 1
Memory mapping, 1
Memset, 1
Message, 1, 2, 3
Message passing, 1, 2, 3
Messagepack, 1
Messages, 1, 2
Methods, 1
miette, 1, 2, 3
mime, 1, 2, 3, 4, 5, 6, 7, 8
MIME type, 1, 2
mime::Mime, 1, 2, 3, 4, 5
minisearch, 1, 2
mio, 1, 2, 3, 4
MIR, 1
miri, 1, 2
mod, 1
Modules, 1
mongodb, 1, 2, 3, 4, 5
monostate, 1, 2, 3, 4, 5, 6
move, 1
Mpmc, 1, 2, 3, 4, 5
MPSC, 1
Mpsc, 1
Msgpack, 1
MSSQL, 1
Multi-consumer channels, 1
multimap, 1
Multiple, 1
Multiple owners, 1, 2
Multithreaded runtimes, 1
Multithreading, 1
Mutable references, 1
Mutex, 1, 2, 3
MySQL, 1
Nacl, 1
nalgebra, 1, 2, 3, 4, 5, 6, 7
nalgebra::Matrix3, 1
Named capture groups, 1
native-tls, 1
native-windows-gui, 1
ndarray, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
ndarray::arr1, 1
ndarray::arr2, 1, 2, 3
ndarray::Array, 1, 2, 3, 4
ndarray::Array1, 1, 2
ndarray::ArrayBase::dot, 1, 2
ndarray::ArrayBase::fold, 1, 2
ndarray::ArrayView1, 1, 2
Network programming, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
Networking applications, 1
Newsletter, 1
Newtype pattern, 1
Nist, 1, 2
No dynamic allocation, 1, 2, 3, 4
No standard library, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53
No-std, 1
No_std, 1, 2, 3, 4
nom, 1, 2
Non-blocking, 1, 2, 3, 4, 5, 6, 7
Non-blocking I/O operations, 1
Non-sequential reads, 1
noplayground, 1
notify, 1, 2, 3, 4
nu-ansi-term, 1
null, 1
Null pointer dereferences, 1
num, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
num-bigint, 1
num-cpus, 1
num-traits, 1
num::BigInt, 1
num::complex::Complex, 1, 2
num_cpus, 1, 2, 3, 4
num_cpus::get, 1, 2, 3
Number, 1
Numerics, 1, 2, 3, 4
nvm, 1
OAuth, 1
Object-safe traits, 1
Once, 1
once_cell, 1, 2, 3, 4, 5
once_cell::sync::Lazy, 1
OnceCell, 1
Online books, 1
open, 1, 2
opencv, 1
openobserve, 1
openrr, 1, 2
Operating system threads, 1
Operating system-specific APIs, 1
Operating systems, 1, 2, 3
Options, 1
Ord, 1
ordered-float, 1
OS, 1, 2, 3, 4, 5, 6, 7, 8
Output coloring and formatting, 1
Output format, 1
Overflow, 1
Ownership, 1, 2
owo-colors, 1
Package, 1
Pagetoc, 1
Paint, 1
Parallel, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Parallel execution, 1
Parallel pipeline, 1
Parallel programming, 1, 2
Parallel sort, 1
Parallel tasks, 1
Parameters, 1
Parent module, 1
parking_lot, 1, 2, 3, 4, 5, 6
parking_lot::Condvar, 1
parking_lot::Mutex, 1
parking_lot::Once, 1
parking_lot::ReentrantMutex, 1
parking_lot::RwLock, 1
Parser, 1
Parser implementations, 1, 2, 3, 4, 5, 6, 7, 8
Parsing, 1
Parsing tools, 1, 2, 3, 4
Password, 1
paste, 1, 2, 3, 4
Path, 1, 2
Pattern, 1, 2, 3
Pattern matching, 1
PBKDF2, 1
Pem, 1
pem-rfc7468, 1, 2
percent_encoding, 1, 2, 3
percent_encoding::percent_decode, 1
percent_encoding::utf8_percent_encode, 1
Performance, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
pest, 1, 2
Phone numbers, 1
pico-args, 1
Piped external commands, 1
Pkcs, 1, 2, 3
pkcs8, 1, 2
plotly, 1
Plugin, 1
Podcasts, 1
polars, 1
Pool, 1
postage, 1, 2, 3
postgres, 1, 2, 3, 4, 5, 6
postgres::Client, 1, 2
postgres::Client::connect, 1
postgres::Client::execute, 1
postgres::Client::query, 1
PostgreSQL, 1
Postgresql, 1
powi, 1
Pre-commit, 1
Pre-release, 1
Precision, 1
Preprocessor, 1, 2, 3
Pretty print, 1
Primitives, 1
Private, 1
Private by default, 1
Private items, 1
proc-macro2, 1, 2
Procedural macro helpers, 1
Process, 1, 2
Producer, 1, 2
Production-ready code, 1
Profile, 1
Profiling, 1
Progress bars and spinners, 1
Promises, 1
prost, 1
Protobuf, 1
protobuf, 1
pub, 1
Pull request, 1
pyenv, 1
pyo3, 1, 2, 3
pyoxidizer, 1
Query, 1
Queue, 1
quickinstall, 1
quote, 1, 2
r3bl_tuify, 1, 2, 3, 4
Rand, 1, 2, 3, 4
rand, 1, 2, 3, 4, 5
rand::distributions::Alphanumeric, 1
rand::distributions::Distribution, 1
rand::distributions::Distribution::sample, 1
rand::distributions::Standard, 1
rand::distributions::uniform::Uniform, 1
rand::Rng, 1, 2
rand::Rng::gen_range, 1, 2
rand_distr, 1, 2, 3, 4
rand_distr::Normal, 1
Random, 1
Random numbers, 1, 2
Random passwords, 1
Random value, 1
Random-number generator, 1
Range, 1
ratatui, 1, 2, 3
rayon, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
rayon::iter::IntoParallelRefIterator::par_iter, 1
rayon::iter::IntoParallelRefIterator::par_iter_mut, 1
rayon::iter::ParallelIterator::any, 1, 2, 3, 4
rayon::iter::ParallelIterator::filter, 1, 2
rayon::iter::ParallelIterator::find_any, 1, 2, 3
rayon::iter::ParallelIterator::map, 1, 2
rayon::iter::ParallelIterator::reduce, 1, 2
rayon::iter::ParallelIterator::sum, 1
rayon::join, 1, 2
rayon::slice::ParallelSliceMut::par_sort_unstable, 1
rayon::spawn, 1
Rc, 1
Rcu, 1, 2, 3
Read/write lock, 1
Recursive, 1, 2, 3, 4
redis, 1, 2
redox, 1
Reference counting, 1
References, 1
Regex, 1
regex, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
regex::Regex, 1
regex::Regex::captures_iter, 1, 2
regex::Regex::replace_all, 1
regex::RegexSetBuilder, 1, 2
regex::Replacer, 1
Regular expressions, 1
Relative path, 1
Rendering, 1, 2
Rendering engine, 1
Replace, 1
Replacement string syntax, 1
Request, 1
reqwest, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
reqwest::blocking::Client::get, 1
reqwest::blocking::Client::head, 1
reqwest::blocking::get, 1
reqwest::blocking::Response, 1
reqwest::Client, 1, 2, 3, 4, 5, 6, 7
reqwest::Client::head, 1
reqwest::Client::post, 1, 2, 3
reqwest::ClientBuilder::build, 1
reqwest::ClientBuilder::timeout, 1
reqwest::Error, 1
reqwest::get, 1, 2, 3, 4, 5, 6
reqwest::header, 1
reqwest::header::HeaderMap::get, 1
reqwest::header::HeaderValue, 1
reqwest::RequestBuilder, 1
reqwest::RequestBuilder::basic_auth, 1
reqwest::RequestBuilder::body, 1
reqwest::RequestBuilder::header, 1
reqwest::RequestBuilder::send, 1, 2, 3
reqwest::Response, 1, 2
reqwest::Response::json, 1
reqwest::Response::url, 1
reqwest::Result, 1
reqwest::StatusCode, 1
reqwest::StatusCode::FORBIDDEN, 1
Return type, 1
Return-position impl Trait in traits, 1
RFC-2822 format, 1
RFC-3339, 1
RFC-3339 format, 1
RFC-7233, 1
rhai, 1, 2, 3, 4, 5
riker, 1
ring, 1, 2, 3, 4, 5, 6, 7
ring::hmac, 1
ring::pbkdf2, 1
ring::pbkdf2::derive, 1
ring::pbkdf2::verify, 1
ring::rand::SecureRandom::fill, 1
ring::signature::Signature, 1
rmp-serde, 1
Robotics, 1
rocket, 1, 2
roogle, 1, 2
roxygen, 1
Rsa, 1, 2, 3, 4
rsa, 1, 2
ruff, 1
rug, 1
rui, 1, 2
Runcmd, 1
Runner, 1
Runtime, 1
rusqlite, 1, 2, 3, 4, 5, 6
rusqlite::Connection, 1, 2
rusqlite::Connection::execute, 1
rusqlite::Connection::last_insert_rowid, 1
rusqlite::Connection::open, 1, 2, 3
rusqlite::Connection::prepare, 1
rusqlite::Connection::transaction, 1
rusqlite::Statement, 1, 2
rusqlite::Statement::query_map, 1
rusqlite::Transaction::commit, 1
Rust, 1
Rust analyzer, 1
Rust binaries installation, 1
Rust book, 1
Rust by example, 1
Rust code examples, 1
Rust installation, 1
Rust interpreter, 1
Rust learning, 1
Rust on Nails, 1
Rust patterns, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
Rust playground, 1, 2, 3
Rust starter pack, 1
Rust tooling, 1, 2
Rust tools, 1
rust_cache, 1
rust_decimal, 1
RUST_LOG, 1, 2, 3, 4
Rustbook, 1
rustc, 1, 2, 3
rustdesk, 1
rustdoc, 1, 2
rustfix, 1
Rustfmt, 1
rustfmt, 1, 2, 3
rustfmt-nightly, 1
rustfmt.toml, 1
rustfmt::skip, 1
rustfmt::skip::attributes(custom_attribute), 1
rustfmt::skip::macros(macro_name), 1
rustls, 1
rustquant, 1, 2, 3, 4, 5, 6
rustup, 1, 2
Rwlock, 1
salsa, 1
Salted passwords, 1
Same, 1
same-file, 1, 2
same_file::Handle, 1
same_file::is_same_file, 1
sccache, 1
Science, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
science, 1
Scope, 1
sea-orm, 1, 2, 3
seaography, 1, 2, 3
Searching, 1
Secp256k1, 1
Secure, 1
Security, 1
Select, 1, 2, 3
select, 1, 2, 3
select::document::Document::find, 1
select::document::Document::from_read, 1
select::node::Node::attr, 1, 2, 3
select::predicate::Name, 1
select::selection::Selection, 1
Semantic Versioning Specification, 1
Semantic versioning specification, 1
semver, 1, 2, 3, 4, 5, 6, 7, 8, 9
semver::Version, 1, 2, 3, 4, 5
semver::Version::parse, 1, 2, 3
semver::VersionReq, 1, 2
semver::VersionReq::matches, 1, 2
Send, 1, 2
Serde, 1, 2, 3
serde, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
serde-ignored, 1
serde::Deserialize, 1, 2
serde_json, 1, 2, 3, 4, 5, 6, 7
serde_json::from_str, 1
serde_json::json, 1
serde_json::to_string, 1, 2
serde_json::Value, 1
Serialization, 1, 2, 3
Serialize, 1
Serialize custom structs, 1
Service, 1, 2
Set, 1
Setter, 1, 2
Settings, 1, 2
SHA-256, 1
sha1, 1, 2, 3
sha2, 1, 2, 3
SHA256, 1
Shadowing, 1
Shared memory concurrency, 1
Shared state, 1
Shell, 1
shields.io, 1
Short-lived thread, 1
Shutdown, 1
shuttle.rs, 1
Signature, 1, 2, 3, 4, 5
Signing, 1
Simple, 1
Simulation, 1
Simultaneous references, 1
sin, 1
Single producer, single consumer, 1
Single-threaded runtime, 1
Siv, 1
sled, 1, 2, 3, 4, 5, 6
Slices, 1
slint, 1, 2, 3, 4
slog, 1
slotmap, 1, 2, 3, 4, 5
smallvec, 1
Smart pointers, 1, 2
smol, 1, 2, 3, 4, 5, 6
sodiumoxide, 1
Sort, 1
sort, 1
Sorting, 1, 2, 3
Span::enter, 1
spawn_blocking, 1, 2, 3
Spawned threads, 1
Spawning tasks, 1
Splay tree, 1
split_at, 1
Spmc, 1
Sql, 1
SQLite, 1
sqlx, 1, 2, 3
sqrt, 1, 2
src, 1
src/lib.rs, 1
src/main.rs, 1
Stack, 1
stakker, 1, 2, 3, 4
starship, 1, 2, 3, 4
Static, 1
staticlib, 1
std, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33
std-core::iter::Iterator::filter_map, 1
std::borrow::Borrow, 1
std::borrow::Cow, 1
std::borrow::Cow::into_owned, 1
std::borrow::ToOwned, 1
std::boxed::Box, 1
std::cell::Cell, 1
std::clone::Clone, 1
std::cmp::Eq, 1
std::cmp::Ord, 1
std::cmp::Ord::cmp, 1
std::cmp::Ordering, 1
std::cmp::PartialEq, 1
std::cmp::PartialOrd, 1
std::cmp::PartialOrd::partial_cmp, 1
std::collections::hash_map::Entry, 1
std::collections::HashMap, 1, 2, 3, 4
std::convert::AsMut, 1
std::convert::AsRef, 1, 2
std::convert::From, 1
std::default::Default, 1, 2
std::env::current_dir, 1
std::env::var, 1
std::error::Error, 1, 2
std::fmt, 1
std::fmt::Debug, 1
std::fmt::Display, 1
std::fs::DirEntry::path, 1
std::fs::File, 1, 2, 3, 4, 5
std::fs::File::create, 1
std::fs::File::open, 1
std::fs::File::seek, 1
std::fs::File::try_clone, 1
std::fs::Metadata, 1
std::fs::Metadata::is_file, 1
std::fs::Metadata::modified, 1
std::fs::read_dir, 1
std::future::Future, 1, 2
std::io::BufRead::lines, 1
std::io::BufReader, 1, 2
std::io::copy, 1
std::io::Error, 1
std::io::Lines, 1
std::io::Read, 1
std::io::Read::read_to_string, 1, 2
std::io::Stderr, 1, 2
std::io::Stdout, 1, 2, 3
std::iter::Iterator::all, 1
std::iter::Iterator::any, 1
std::iter::Iterator::filter, 1
std::iter::Iterator::find, 1
std::iter::Iterator::fold, 1
std::iter::Iterator::max_by_key, 1
std::iter::Iterator::sum, 1, 2
std::marker::Copy, 1
std::marker::Send, 1
std::marker::Sync, 1
std::marker::Unpin, 1
std::ops::Deref, 1, 2, 3
std::ops::Drop, 1, 2
std::ops::Fn, 1
std::ops::FnMut, 1
std::ops::FnOnce, 1
std::ops::FromResidual, 1
std::option::Option, 1, 2, 3, 4, 5, 6
std::option::Option::as_deref, 1
std::option::Option::as_deref_mut, 1
std::option::Option::expect, 1
std::option::Option::None, 1, 2, 3, 4
std::option::Option::Some, 1
std::option::Option::unwrap, 1
std::option::Option::unwrap_or, 1
std::option::Option::unwrap_or_default, 1
std::option::Option::unwrap_or_else, 1
std::panic::RefUnwindSafe, 1
std::panic::UnwindSafe, 1
std::path::Path::strip_prefix, 1
std::process::Command, 1, 2, 3, 4, 5
std::process::Output, 1, 2
std::process::Stdio, 1
std::result::Result::unwrap_or, 1
std::str::FromStr, 1
std::string::String, 1, 2, 3, 4, 5
std::string::ToString, 1
std::sync::Arc, 1
std::sync::atomic, 1
std::sync::atomic::AtomicBool, 1
std::sync::atomic::AtomicI8, 1
std::sync::atomic::AtomicIsize, 1
std::sync::atomic::AtomicU16, 1
std::sync::atomic::AtomicUsize, 1
std::sync::mpsc, 1
std::sync::mpsc::channel, 1
std::sync::mpsc::Receiver::recv, 1
std::sync::Mutex, 1, 2, 3, 4, 5
std::sync::MutexGuard, 1
std::sync::RwLock, 1
std::task::Poll, 1
std::thread::spawn, 1
std::time::Duration, 1
std::time::Duration::as_secs, 1
std::time::Instant, 1
std::time::Instant::elapsed, 1, 2
std::time::Instant::now, 1
std::time::SystemTime::elapsed, 1
std::vec::Vec::sort, 1
std::vec::Vec::sort_by, 1
std::vec::Vec::sort_unstable, 1
stdout, 1
stdx, 1
stork-search, 1, 2, 3
Stream, 1
Strings, 1
Strip, 1
Struct, 1
structopt, 1
Structs, 1, 2
Structured, 1
stylish, 1
Subcommand, 1, 2
Submodules, 1
subtle, 1
SUMMARY.md, 1
swc_ecma_parser, 1
Symbolic links, 1
syn, 1, 2, 3
Sync}{{hi:'static, 1
Synchronization, 1
Synchronous code, 1, 2
Synchronous IO, 1
syslog, 1, 2, 3
syslog::init, 1, 2, 3
Table, 1
tantivy, 1, 2, 3
tar, 1, 2, 3, 4
tar::Archive::entries, 1
tar::Archive::unpack, 1
tar::Builder, 1
tar::Builder::append_dir_all, 1
tar::Entry, 1
tar::Entry::unpack, 1
Task, 1
tauri, 1, 2, 3
Tempfile, 1
tempfile, 1, 2, 3, 4
tempfile::Builder, 1
tempfile::Builder::tempdir, 1
Template, 1, 2, 3
Template engine, 1, 2, 3, 4, 5
Template engines, 1, 2
Tera, 1
tera, 1
termbook, 1, 2
termcolor, 1
Terminal, 1, 2, 3, 4, 5, 6, 7
termion, 1
Testing, 1, 2, 3, 4
Text, 1, 2
Text editors, 1, 2
Text processing, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
Theme, 1
thiserror, 1, 2, 3, 4, 5, 6
Thread, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
Thread pools, 1, 2, 3, 4
threadpool, 1, 2, 3, 4, 5
threadpool::ThreadPool, 1
threadpool::ThreadPool::execute, 1
Threads, 1, 2
Tickers, 1
Time, 1
time, 1, 2
Time since last modification, 1
Timeouts, 1
Timestamp, 1
timezones, 1
tinysearch, 1, 2
tinytemplate, 1
tinyvec, 1
to_radians, 1
toasty, 1
Toc, 1
Tokio, 1
tokio, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
Tokio examples, 1
tokio-graceful-shutdown, 1, 2, 3
Tokio-postgres, 1
tokio-postgres, 1
tokio::main, 1, 2
tokio::main::current-thread-runtime, 1
tokio::spawn, 1
tokio::sync::oneshot, 1
tokio::task::LocalSet, 1
tokio::task::spawn, 1, 2
tokio::task::spawn_blocking, 1, 2
tokio_graceful_shutdown, 1, 2
TOML, 1
toml, 1, 2, 3, 4, 5, 6
tonic, 1, 2, 3, 4, 5, 6
tower, 1, 2, 3, 4, 5, 6, 7
tower-http, 1, 2, 3, 4
tower::Layer, 1, 2, 3
tower::Service, 1, 2
tower::ServiceBuilder, 1
tower_http, 1, 2, 3
Tracing, 1, 2
tracing, 1, 2, 3, 4, 5, 6
tracing-subscriber, 1, 2, 3
tracing::span::Span::in_scope, 1
tracing_journald, 1
tracing_subscriber, 1, 2
Trait, 1
Trait objects, 1
Traits, 1, 2, 3
trillium, 1, 2, 3, 4, 5
Tty, 1
Tui, 1
tui, 1, 2, 3
typed-builder, 1
typesense, 1, 2
u128, 1
u16, 1
u32, 1
u8, 1, 2
udeps, 1
Unicode, 1
unicode-segmentation, 1
unicode_segmentation, 1, 2
unicode_segmentation::UnicodeSegmentation::graphemes, 1
Uniform distribution, 1, 2
Unit type, 1
Unsized, 1
Unused variable, 1
ureq, 1
URL, 1
url, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
url::ParseOptions, 1
url::PathSegmentsMut::clear, 1
url::Position, 1
url::Url, 1, 2, 3, 4
url::Url::join, 1
url::Url::origin, 1
url::Url::parse, 1
url::Url::parse_with_params, 1
url::Url::set_query, 1
use, 1
usize, 1, 2
UTC, 1
UTF-8, 1
Utilities, 1
Utility, 1
uuid, 1
Value formatting, 1, 2, 3
Values, 1
Variables, 1, 2
Variants, 1
Vec, 1
Vector, 1, 2
Vectors, 1
Version number, 1, 2
Version requirements, 1
Version string, 1
Visibility, 1
Volatile, 1
VS code, 1, 2, 3
Vtable, 1
Walk, 1, 2, 3, 4
walkdir, 1, 2, 3, 4, 5, 6, 7
walkdir::IntoIter::filter_entry, 1, 2
walkdir::WalkDir::follow_links, 1
walkdir::WalkDir::max_depth, 1
walkdir::WalkDir::min_depth, 1
walkdir::WalkDir::new, 1, 2
wasmtime, 1
Watch, 1
watchmaker, 1
watt, 1, 2, 3
Web, 1, 2
Web programming, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
WebAssembly, 1, 2, 3, 4
WebSocket, 1, 2
wgpu, 1
while let, 1
Win, 1
Wincon, 1
Windows, 1
windows, 1, 2
Windows APIs, 1
Word, 1
Work-stealing runtime, 1
Workspace-hack, 1
wrapping_add, 1
Wyrand, 1
x509-cert, 1, 2
Xdg, 1, 2
xilem, 1, 2, 3, 4, 5
xshell, 1, 2, 3, 4, 5
xsv, 1
yansi, 1
year/month/day/weekday, 1
yew, 1, 2, 3, 4
zed, 1, 2, 3
zenoh, 1, 2
Zero, 1
zerocopy, 1
zeroize, 1
Zig, 1
zola, 1, 2

Thanks

This reference guide is written and edited by John CD.

It is the successor of and incorporates most of the Rust Cookbook. Thanks to its many contributors.