What you will find here

This book is a compendium of Rust ecosystem examples, recipes, and links. It is intended to be everything you need for day-to-day Rust coding, in one place.

It quickly summarizes the basics of the language and often-used elements of the standard library.

It then focuses on cross-cutting concerns that affect most aspects of development e.g. error handling, error customization, configuration, logging...

Concurrency, including asynchronous programming, are covered in details.

Next are tools, such as Cargo, Clippy, Rustfmt, as well as links and examples specific to programming domains such as CLI and Web development. The links section provides pointers to notable Rust websites, books, and code examples.

Who should read this book?

This book is intended both for new Rust programmers (to get an overview of the capabilities of the Rust ecosystem and pointers to other resources) and for 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.

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 almost more than 100 thousand crates 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 (not yet) dominant frameworks or platforms like Rails, Django, Spring or Node in the Rust world.

This book intends to provide EXAMPLES to demonstrate the use of key Rust crates, examples which are absent from or scattered in the typically dry reference docs⮳, and hopes to become a "cheat sheet on steroid" for the Rust ecosystem (not just the Rust language).

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 libraries.

The Rust cookbook⮳ demonstrate good practices to accomplish common programming tasks, using the crates of the Rust ecosystem. It focuses mainly on std and a few core crates.

Call for contributions

This book is in its early days - feel free to submit an issue or a pull request to the repo.

Contributions, from small edits to whole chapters, are most welcome. Draft pages are kept in this folder. An informal (and very long) list of topics of interest is kept in TODO. Embedded examples should be ideally runnable on the Rust playground⮳ or at least directly copy-pasteable into Rust code. Please read CONTRIBUTING.md for more details.

Its 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.

This site is not affiliated with the Rust Foundation⮳.

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.

Main function

fn main() {
    println!("Hello, world!");
}

Async Main Function

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

Simple Data Types

  • 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;

Overflow handling

  • Wrap in all modes with the wrapping_* methods, such as wrapping_add.
  • Return the 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

#![allow(unused)]
fn main() {
    // `const` is set to a constant expression; the type must be annotated
    const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

    let apples = 5; // immutable variable

    let mut guess = String::new(); // mutable variable
}

Shadowing

fn main() {
    let x = 5; // x is immutable

    let x = x + 1; // redefines x
    println!("{x}");

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

Destructuring

fn main() {
    // destructuring tuples
    let (_x, _y, _z) = (1, 2, 3);

    // destructuring structs
    struct Point {
        x: i32,
        y: i32,
    }

    let p = Point { x: 0, y: 7 };
    let Point { x: _a, y: _b } = p; // a = 0, b = 7
    let Point { x, y } = p; // simpler
    let _ = (x, y);
}

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

Ownership and Borrowing

Ownership

  • No garbage collector. Ownership instead.
  • Each value in Rust has an owner.
  • There can only be one owner at a time.
fn main() {
    let s1 = String::from("hello"); // On the heap
    let _s2 = s1; // s1 was MOVED into s2 - NOT a shallow copy - Rust invalidates s1
    // ERROR println!("{}, world!", s1);
}

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

fn main() {
    {
        let _s = String::from("hello");
    } // variable out of scope - Rust calls `drop`

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

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

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

If a type implements the 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); // pass an immutable reference to s1

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

Mutable reference

fn change(some_string: &mut String) {
    // note the `&mut`
    some_string.push_str(", world");
}

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

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

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!("got t");
}

// 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. t must be
    // some kind of 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() -> ! {
    // <-- empty type
    panic!("This call never returns.");
}

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

Control Flow

If else

fn main() {
    let number = 3;
    let result = if number < 5 {
        // condition must return a bool; `if` is an expression
        println!("condition was true");
        5
    } 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

#![allow(dead_code)]

struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

fn main() {
    // create an instance
    let _user1 = User {
        active: true,
        username: String::from("someusername123"),
        email: String::from("someone@example.com"),
        sign_in_count: 1,
    };
}

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

#![allow(dead_code)]

struct User {
    active: bool,
    username: String,
    email: String,
}

fn build_user(email: String, username: String) -> User {
    User {
        active: true,
        username, // instead of username: username - field init shorthand
        email,    // same
    }
}

fn main() {
    let user1 = build_user("<user@example.com>".into(), "user".to_string());

    // struct update
    let _user2 = User {
        email: String::from("another@example.com"),
        ..user1 /* the remaining fields not explicitly set should have the
                 * same value as the fields in the given instance. */
    };
}
// Tuple struct
struct Color(i32, i32, i32);

// Unit-like struct
struct AlwaysEqual; // <-- no fields

fn main() {
    let _black = Color(0, 0, 0);
    let _s = AlwaysEqual;
}
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

#![allow(dead_code)]

enum Message {
    Quit,
    Move { x: i32, y: i32 }, // struct-like
    Write(String),           // tuple-like
    ChangeColor(i32, i32, i32),
}

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

fn main() {
    let _home = Message::ChangeColor(127, 0, 0); // <-- note the ::
}

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
    }
}

fn main() {}

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();
}

// BEWARE: supertrait are NOT inheritance!

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

#![allow(dead_code)]

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;

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

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

Associated types

trait Iterator {
    type Item; // <-- associated type
    // in Impl, use e.g. `Iterator<Item = u32>`

    fn next(&mut self) -> Option<Self::Item>;
}

// Generic type with default
trait Add<Rhs = Self> {
    type Output; // <-- associated type

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

fn main() {}

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 Box (which points onto the heap) or & (which can point anywhere).

A type like &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 is made up of an object-safe-reference⮳ base trait plus any number of autotraits (one of Send, Sync, Unpin, UnwindSafe, and RefUnwindSafe - see special traits⮳).

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

See also

Trait Objects (docs)

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)]

use std::thread;

pub struct S;

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

fn dead_code() {}

fn main() {
    let x = 1;
    let mut m = 2;
}

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

//! Crate documentation
#![warn(
    unused,
    missing_debug_implementations,
    missing_copy_implementations,
    missing_docs,
    rust_2018_idioms
)]
#![deny(unreachable_pub)] // error if violation
#![forbid(unsafe_code)] // same as `deny` +forbids changing the lint level afterwards

/// This is the required documentation for S
#[derive(Debug, Copy, Clone)]
pub struct S;

/// Here is the main function!
fn main() {
    let _ = S;
}

You also apply these attributes to specific functions:

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

fn main() {}

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

Automatic trait derivation

See Automatic derivation

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));
}

Deprecated

#![allow(deprecated)] // Removes the warning.

#[deprecated(since = "5.2.0", note = "use bar instead")]
pub fn foo() {}

fn main() {
    foo();
}

Conditional Compilation

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

Generic Structs

#![allow(dead_code)]

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

Prevent dangling references.

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

fn main() {
let _s: &'static str = "I have a static lifetime.";
}

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

#![allow(dead_code)]
struct ImportantExcerpt<'a> {
    part: &'a str,
}

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

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

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.

visibility-rules-rust-by-example-badge

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.

use-rust-by-example-badge

#![allow(unused_imports, dead_code)]

// For code from an external crate, the absolute path begins with the crate name
// Here, the standard `std` library
use std::collections::HashMap;
// With the following, `File` without prefix is available in the scope
use std::fs::File;
// Glob - all public objects in `collections` are now in scope
use std::collections::*;
// Combine multiple `use` lines together with { }
use std::{cmp::Ordering, fmt};
// The following is equivalent to `use std::io; use std::io::Write;`
use std::io::{self, Write};

mod utils {
    pub fn insert_use() {}
}
// Absolute path - for code from the current crate, it starts with the literal crate.
use crate::utils::insert_use;

mod a {
    pub mod b {}
}
// A relative path starts from the current module and uses `self`, `super`,
// or an identifier in the current module.
use self::a::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;
}

use std::fmt::Result;
use std::io::Result as IoResult; // Alias

mod front_of_house {
    pub mod hosting {}
}
// Reexporting - `pub use` re-exports the `hosting` module from the root module,
// thus external code can use the path `<crate>::hosting::add_to_waitlist()`
// instead of `crate::front_of_house::hosting::add_to_waitlist()`.
pub use crate::front_of_house::hosting;

fn main() {}

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

use front_of_house::hosting;

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

fn eat_at_restaurant() {
    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.

use std::collections::HashMap;

fn main() {
    let mut _map: HashMap<u32, String> = HashMap::new();
}

Match, if let, while let

#![allow(dead_code)]

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));
}
#![allow(dead_code)]
#![allow(clippy::match_single_binding)]

// 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,}.

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

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

#![allow(dead_code)]

// 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"));
}
  • Fn: the closure uses the captured value by reference (&T)
  • FnMut: the closure uses the captured value by mutable reference (&mut T)
  • FnOnce: the closure uses the captured value by value (T)

Functions may also be used as arguments.

Iterators

#![allow(clippy::useless_vec)]

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 - Macros

The Little Book of Rust Macros

#![allow(clippy::useless_vec)]

fn main() {
    // Used as an expression.
    let _x = vec![1, 2, 3];

    // Used as a statement.
    println!("Hello!");

    // Used in a pattern.
    macro_rules! pat {
        ($i:ident) => {
            Some($i)
        };
    }

    if let pat!(x) = Some(1) {
        assert_eq!(x, 1);
    }
}

// Used in a type.
macro_rules! Tuple {
    { $A:ty, $B:ty } => { ($A, $B) };
}

type _N2 = Tuple!(i32, i32);

// Used as an item.
// thread_local!(static FOO: RefCell<u32> = RefCell::new(1));

// Used as an associated item.
macro_rules! const_maker {
    ($t:ty, $v:tt) => {
        const CONST: $t = $v;
    };
}

trait T {
    const_maker! {i32, 7}
}

// Macro calls within macros.
macro_rules! _example {
    () => {
        println!("Macro call in a macro!")
    };
}

// Outer macro `example` is expanded, then inner macro `println` is
// expanded. example!();

Key crates

paste-badge (github)

Paste provides a flexible way to paste together identifiers in a macro, including using pasted identifiers to define new items.

proc-macro2-badge (github) (workshop)

proc-macro2⮳ bring proc-macro-like functionality to other contexts like build.rs and main.rs and makes procedural macros unit testable.

syn-badge (github)

Syn is a parsing library for parsing a stream of Rust tokens into a syntax tree of Rust source code.

quote-badge

Quote provides the quote! macro for turning Rust syntax tree data structures into tokens of source code.

Tools

Cargo expand(github)

See also

proc macro workshop

watt-badge (github)

Standard Library

std-badge

Option

std-badge

Rust has no null. Instead, use Option⮳:

#![allow(unused)]
enum Option<T> {
    None,
    Some(T),
}
fn main() {}

Every Option is either Some and contains a value, or 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 base...");

    // 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() {
    bake_cake(Some("rainbow nonpareils"));
}

Adapters for working with references

  • as_ref converts from &Option<T> to Option<&T>
  • as_mut converts from &mut Option<T> to Option<&mut T>
  • as_deref converts from &Option<T> to Option<&T::Target>
  • as_deref_mut converts from &mut Option<T> to Option<&mut T::Target>

Extracting the value contained in Option

These methods extract the contained value in an Option<T> when it is the Some variant. If the Option is None:

  • expect panics with a provided custom message
  • unwrap panics with a generic message
  • unwrap_or returns the provided default value
  • unwrap_or_default returns the default value of the type T (which must implement the Default trait)
  • unwrap_or_else returns the result of evaluating the provided function

Combinators

#![allow(clippy::bind_instead_of_map)]

use std::fs;

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

fn main() {
    let contents = read_file("poem.txt");

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

Vectors

std-badge

Vectors can only store values that are the same type.

#![allow(clippy::vec_init_then_push)]

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

std-badge

All of the 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

std-badge

String type

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

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

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

Use {:?} to use the 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).

String concatenation

Here are several common methods to concatenate Strings:

#![allow(clippy::vec_init_then_push)]

#[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();

    #[allow(clippy::useless_vec)]
    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);
}

Examples from concatenation_benchmarks-rs

Smart Pointers

std-badge

  • 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

box-rust-book-badge

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.

The Box<T> type is a smart pointer because it implements the Deref trait, which allows Box<T> values to be treated like references. Implementing the Deref trait allows you to customize the behavior of the dereference operator *.

Use when

  • you have a 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.

Rc

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

RefCell

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.

Key crates

Consult the following sites for crate recommendations:

or older resources, such as:

In this section

See also

base64-badge bitflags-badge byteorder-badge cc-badge data-encoding-badge flate2-badge glob-badge image-badge memmap-badge mime-badge nalgebra-badge ndarray-badge num-badge num-cpus-badge percent-encoding-badge rand-badge rand-distr-badge ring-badge same-file-badge select-badge semver-badge syslog-badge tar-badge tempdir-badge toml-badge url-badge unicode-segmentation-badge walkdir-badge

Regex Crate

regex-badge (github)

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.
    // [^!] excludes !
    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
    // [text] not preceded by ! or ], not followed by [ or <spaces>[ or (
    // 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());
}

Serialization

serde-badge

See also

serde-json-badge (lib.rs)

Monostate for serde (github)

Serde-ignored (github)

Time and Date

chrono-badge

Cross-cutting concerns

Automatic trait derivation

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.

Derive More

derive-more-badge

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(fmt = "int: {}", _0)]
    Int(i32),
    Uint(u32),
    #[display(fmt = "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");
}

Error Handling

Irrecoverable panics

fn main() {
    panic!("crash and burn");
}

Recoverable errors with Result

use std::fs::File;
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 = File::open("hello.txt").unwrap();
}

unwrap_or_else

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let _greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("Problem creating the file: {:?}", error);
            })
        } else {
            panic!("Problem opening the file: {:?}", error);
        }
    });
}

A Shortcut for Propagating Errors: the ? Operator

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("hello.txt")?;
    let mut username = String::new();
    username_file.read_to_string(&mut username)?;
    Ok(username)
}

fn main() {
    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 FromResidual.

Another example:

use std::error::Error;

fn parse_port(s: &str) -> Result<u16, Box<dyn Error>> {
    // needed 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).
    let port: u16 = s.parse()?;
    if port == 0 {
        Err(Box::from(format!("invalid: {}", 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>;

Custom Errors

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-badge (crates.io)

Use Result<T, anyhow::Error> or equivalently anyhow::Result<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

    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-badge

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>> {
    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-badge (lib.rs) prints fancy diagnostics upon error.

// 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 URK 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,
        // 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 just 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(())
}
mod mylib;

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(())
}

See also

eyre-badge

error-chain-badge

Do not use Error Chain⮳, which is deprecated.

Configuration

Environment variables

dotenvy-badge

dotenvy⮳ supersedes dotenv⮳.

use std::env;
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    // Load environment variables from .env file.
    // Fails if .env file not found, not readable or invalid.
    dotenvy::dotenv()?;

    for (key, value) in env::vars() {
        println!("{key}: {value}");
    }

    Ok(())
}

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-badge

Envy can deserialize environment variables into typesafe struct.

[dependencies]
envy = "0.4"
serde = { version = "1.0", features = ["derive"] }
#![allow(dead_code)]

use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Configuration {
    port: u16,
    items_per_page: u16,
}

fn main() {
    let c = envy::from_env::<Configuration>()
        .expect("Please provide PORT and ITEMS_PER_PAGE env vars");

    let c2 = envy::prefixed("MY_APP__")
        .from_env::<Configuration>()
        .expect(
            "Please provide MY_APP__PORT and MY_APP__ITEMS_PER_PAGE env vars",
        );

    println!("c: {:?} c2: {:?}", c, c2);
}

Config

config-badge

Config is a layered configuration system for Rust applications. It reads from JSON, TOML, YAML, INI, RON, JSON5 files.

Confy

confy-badge

use serde::Deserialize;
use serde::Serialize;

#[derive(Serialize, Deserialize)]
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)?;
    Ok(())
}

See also

dotenv-badge

Logs

tracing-badge (github)

tracing-subscriber-badge (crates.io)

Add to Cargo.toml

[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"

Initialization

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();
}

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();
}

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::debug;
use tracing::error;
use tracing::event;
use tracing::info;
use tracing::trace;
use tracing::warn;
use tracing::Level;

fn main() {
    event!(Level::INFO, "something happened");
    error!("!");
    warn!("!");
    info!("!");
    debug!("!");
    trace!("!");

    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);

    // ?: `my_struct` will be recorded
    // using its `fmt::Debug` implementation.
    let my_struct = S;
    event!(Level::TRACE, greeting = ?my_struct);
}

#[derive(Debug)]
struct S;

Spans

use tracing::span;
use tracing::Level;

fn main() {
    let span = span!(Level::TRACE, "my_span");
    // `enter` returns a RAII guard which, when dropped, exits the span.
    // This indicates that we are in the span
    // for the current lexical scope.
    {
        let _guard = span.enter();
        // Any trace events that occur here will occur within the
        // span.
    }
    // Dropping the guard exits the span.
}

One-liner with .entered():

use tracing::span;
use tracing::Level;

fn main() {
    let span = span!(Level::TRACE, "some span").entered();

    // code here is within the span

    // optionally, explicitly exit the span, returning it
    let span = span.exit();

    // code here is no longer within the span

    // enter the span again
    let _span = span.entered();
}

Holding the drop guard returned by Span::enter across .await points will result in incorrect traces. Use in_scope

use tracing::debug_span;
use tracing::info_span;
use tracing::Instrument;

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::event;
use tracing::instrument;
use tracing::Level;

#[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;
}

OpenTelemetry

OpenTelemetry Rust documentation

See also

env-logger-badge

log-badge

log4rs-badge

Databases and ORMs

Sqlx

sqlx-badge (github) (lib.rs)

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.

SeaORM

sea-orm-badge (website) (cookbook)

Seaography GraphQL server

Diesel

diesel-badge (lib.rs)

See also

postgres-badge

rusqlite-badge

Tests

cargo test to run all tests. cargo test test_prefix to run all tests that start with the provided prefix. cargo test -- --show-output to show output (println!) that is otherwise captured during tests.

#![allow(dead_code)]

// Put unit tests in the same file than the main code

#[cfg(test)] // only for unit tests
mod tests {
    // Access to all objects in the parent module,
    // which contains the main code
    use super::*;

    // Test functions must be free, monomorphic functions that take no
    // arguments, and commonly return () or Result<T, E> where T:
    // Termination, E: Debug
    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle {
            width: 8,
            height: 7,
        };
        let smaller = Rectangle {
            width: 5,
            height: 1,
        };

        assert!(larger.can_hold(&smaller));
        // or assert_eq!(result, some_const);
        // or assert_ne!
    }

    // This test passes if the code inside the function panics;
    // It fails if the code inside the function doesn’t panic.
    #[test]
    #[should_panic]
    fn another() {
        panic!("Make this test fail");
    }

    // With Result
    #[test]
    fn it_works() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(()) // Pass if OK
        } else {
            Err(String::from("two plus two does not equal four"))
        }
    }

    #[test]
    #[ignore]
    fn expensive_test() {
        // Code that takes an hour to run
    }
}

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn can_hold(&self, _another: &Rectangle) -> bool {
        true
    }
}

fn main() {}

Custom message

#[test]
#[should_panic]
fn custom_message() {
    let result = "Carl";

    assert!(
        result.contains("Carol"),
        "Greeting did not contain name, value was `{}`",
        result
    );
}

fn main() {}

See Also

approx-badge Approx

cargo-nextest⮳: cargo nextest run; cargo test --doc

Lazy Init

std-badge

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());
    assert_eq!(value, "Hello, World!");
    assert!(cell.get().is_some());
}

Older library

once-cell-badge (lib.rs)

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 Lazy<T> 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());
}

See also

lazy-static-badge

Performance

Rust Performance Book (GitHub)

Incremental Computation

Salsa (GitHub)⮳ is a framework for on-demand, incrementalized computation.

Documentation

The rustdoc book

docs.rs⮳: open-source documentation host for Rust crates.

Documenting your code

  • Add documentation comments to your code.
/// This is a doc comment. It is equivalent to the next line.
#[doc = r" This is a doc comment."]
fn main() {}

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(),
    }
}

struct Person {
    name: String,
}

fn main() {
    let _ = new("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.

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() {}
  • 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() {}

Badges

Shield.io

Concurrency

This section covers concurrent programming, specifically parallel programming and async programming.

Parallelism

  • 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: Independent units of execution that can be spawned using e.g. std::thread::spawn.
  • Mutexes: Protect shared data from race conditions using e.g. std::sync::Mutex.
  • Channels: Allow threads to communicate and exchange data using e.g. std::sync::mpsc.

Here are the topics we’ll cover:

Asynchronous programming

  • 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.
  • Multithreaded async programming also requires careful synchronization to prevent data races.

Key constructs in Rust:

  • async / await keywords
  • Futures

Here are the topics we’ll cover:

See Also

concurrency-rust-book-badge

Multithreading

Spawn, join

std-badge

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");
}

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

Scoped threads

std-badge

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);
    }
}

Rayon - parallel processing

rayon-badge

Rayon (github)

Parallel iteration

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[..]));
}

Parallel sorting

use rayon::prelude::*;

fn main() {
    let mut v = [-5, 4, 1, -3, 2];
    v.par_sort();
    println!("{:#?}", v);
}

Custom parallel tasks

Rayon implements join⮳, scope⮳, 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
}

See also

threadpool-badge

Message passing

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-badge

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-badge

Multi-producer multi-consumer channels for message passing.

use std::thread;

use crossbeam_channel::unbounded;
use crossbeam_channel::RecvError;
use crossbeam_channel::TryRecvError;

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,
        }
    }
}

See also

crossbeam-badge

postage-badge

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.

Mutex

std-badge

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-badge (crates.io)

Parking Lot⮳ provides implementations of Mutex, RwLock, Condvar and Once that are smaller, faster and more flexible than those in the Rust standard library. It also provides a ReentrantMutex type.

std::sync::Mutex works fine, but Parking Lot is faster.

use parking_lot::Once;

static START: Once = Once::new();

fn main() {
    // run a one-time initialization
    START.call_once(|| {
        // run initialization here
    });
}
use parking_lot::RwLock;

fn main() {
    let lock = RwLock::new(5);

    // many reader locks can be held at once
    {
        let r1 = lock.read();
        let r2 = lock.read();
        assert_eq!(*r1, 5);
        assert_eq!(*r2, 5);
    } // read locks are dropped at this point

    // only one write lock may be held, however
    {
        let mut w = lock.write();
        *w += 1;
        assert_eq!(*w, 6);
    } // write lock is dropped here
}

Atomics

std-badge crossbeam-badge

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 AtomicBool, AtomicIsize, AtomicUsize, AtomicI8, 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 Arc (an atomically-reference-counted shared pointer).

crossbeam⮳ also offers AtomicCell, a thread-safe mutable memory location. This type is equivalent to 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);
}

Concurrent Data Structures

Dashmap

dashmap-badge

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 => {
                    if let Some(value) = map_clone.get("key1") {
                        println!("Thread {} read key1: {}", thread_id, *value);
                    } else {
                        println!("Thread {} couldn't find key1", thread_id);
                    }
                }
                3 => {
                    if let Some(mut value) = map_clone.get_mut("key2") {
                        *value += 10;
                        println!(
                            "Thread {} incremented key2 value to {}",
                            thread_id, *value
                        );
                    } else {
                        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

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'));
}

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

Are we async yet?

Asynchronous Programming in Rust (book)

Basic Example

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 {
    // ...
    SomeStruct
}

async fn second_task_1(_s: &SomeStruct) { /* ... */
}

// `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 {} // 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 =
// ["full"] }` Technically, the #[tokio::main] attribute is a macro
// 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

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 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 multithreaded runtimes are available.

Which crate provides what?

  • The async/await syntaxic sugar is supported directly by the Rust compiler.
  • The most fundamental traits, types, and functions, such as the 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

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

Alternatives to the Tokio async ecosystem include:

  • async-std-badge async-std⮳: async version of the Rust standard library. No longer maintained?
  • smol-badge Smol
  • embassy-badge Embassy⮳ for embedded systems.
  • mio-badge 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.

Async traits

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;
    }
}

async fn do_async_op() -> bool {
    true
}

async fn log_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.

trait Container {
    fn items(&self) -> impl Iterator<Item = u8>; // <-- return Impl in a trait
}

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-badge Async trait crate (github).

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) {}

    async fn hide_for_now(&self) {}
}

async fn remind_user_to_join_mailing_list() {}

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

Tokio

tokio-badge

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.

Graceful shutdown

tokio-graceful-shutdown-badge

Example from tokio_graceful_shutdown⮳:

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

async fn countdown() {
    for i in (1..=5).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() -> std::result::Result<(), Box<dyn std::error::Error>> {
    // Init logging
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::TRACE)
        .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 channels.

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

OneShot

tokio-badge

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();
}

Multiple Producer, Single Consumer

tokio-badge

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;
}

See also

async-channel-badge

postage-badge (lib.rs)

Streams

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

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

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 map, filter, and fold, and their early-exit-on-error cousins try_map, try_filter, and try_fold.

To process multiple items from a stream concurrently, use the for_each_concurrent and try_for_each_concurrent methods:

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

type Result = std::result::Result<(), Box<dyn std::error::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 = ["file1.txt"]; // add more here...

    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 {
            if let Err(e) = fut.await {
                println!("Error: {}", e);
                if let Some(source) = e.source() {
                    println!("  Caused by: {}", source);
                }
            }
        },
    );

    fut.await;

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

See also Tokio async-stream (GitHub)async-stream-badge.

Futures crate

futures-badge crates.io

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

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.

#![allow(dead_code)]

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

use futures::join;

async fn foo(i: u32) -> u32 {
    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

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

#![allow(clippy::async_yields_async)]
use futures::future::FutureExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    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 });
    assert_eq!(future_of_7.await, 7);

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

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

See also

futures-executor-badge

Mixing Async and Blocking Code

Calling blocking code from async code

  • 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 spawn_blocking function, use rayon, or spawn a dedicated thread.

See Async: What is blocking? blog post⮳.

Tokio spawn_blocking

Use 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();
}

Using the rayon crate

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

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 code

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.

Futures executor

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

async fn do_something() {}

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
}

Using the Tokio runtime directly

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 excute
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);
}

Web

References

Are we Web yet?

Building a crawler in Rust: Design and Associated Types

Axum

Crates.io example source code (using Axum)

Actix

actix-web-badge!

Actix web

Actix examples

Auth Web Microservice with rust using Actix-Web 4.0 - Complete Tutorial

Practical Rust Web Development - API Rest

Other Web Frameworks

Loco

Loco.rs

Loco article

Rocket

Rust + Rocket RealWorld framework implementation

See also

hyper-badge tonic-badge

Rust on Nails

Building a SaaS with Rust and Next.js

Static Website Generators

Zola

Middleware

Tower

tower-badge

Tower⮳ is a library of modular and reusable components for building robust networking clients and servers.

Tower provides a simple core abstraction, the 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 Layer trait, is used to compose middleware with Services. A Layer is a function taking a Service of one type and returning a Service of a different type. The ServiceBuilder type is used to add middleware to a service by composing it with multiple Layers. The 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 Service trait: hyper⮳, tonic (gRPC)⮳.

Building a Tower middleware from scratch

Tower HTTP

tower-http-badge

Tower HTTP⮳ contains HTTP specific Tower utilities.

#![allow(dead_code)]

use std::iter::once;
use std::sync::Arc;

use bytes::Bytes;
use http::header::HeaderName;
use http::header::AUTHORIZATION;
use http::header::CONTENT_TYPE;
use http::Request;
use http::Response;
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);
}

Alternatives

Trillium

CORS

CORS (mozilla)

Using the Tower ecosystem:

use std::convert::Infallible;
use std::error::Error;

use bytes::Bytes;
use http::header;
use http::Method;
use http::Request;
use http::Response;
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<(), Box<dyn Error>> {
    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?;

    assert_eq!(
        response
            .headers()
            .get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
            .unwrap(),
        "*",
    );
    Ok(())
}

HTTP clients

reqwest-badge

Other Domains

CLI

Command Line Applications in Rust (book)

Command-line argument parsing

clap-badge (tutorial) (cookbook) (examples)

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](https://docs.rs/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

ansi-term-badge

TUIFY YOUR CLAP CLI APPS AND MAKE THEM MORE INTERACTIVE

WASM

Rust and WebAssembly (book)

Yew

yew-badge

What is Yew?

Example real world app built with Rust + Yew + WebAssembly

GUI

Are we GUI yet?

GTK and Tauri are probably the only options which can be described as production-ready without caveats. The Rust native options are usable for simple projects but are all still quite incomplete.

See the relevant section in blessed.rs

Tauri

tauri-badge (website)

Tauri⮳ is an app construction toolkit that lets you build software for all major desktop operating systems using web technologies. It is similar to Electron.

egui

egui-badge

egui⮳ is an easy-to-use immediate mode GUI that runs on both web and native. egui aims to be the best choice when you want a simple way to create a GUI, or you want to add a GUI to a game engine.

Other GUI frameworks

slint-badge (website)

iced-rs-badge (github) is a cross-platform GUI library for Rust, inspired by Elm.

druid-badge (github) is a data-first Rust-native UI design toolkit (experimental).

Cross-platform Applications

Crux

crux-badge

Crux(GitHub)⮳ is an experimental approach to building cross-platform applications.

It splits the application into two distinct parts, a Core built in Rust, which drives as much of the business logic as possible, and a Shell, built in the platform native language (Swift, Kotlin, TypeScript), which provides all interfaces with the external world, including the human user, and acts as a platform on which the core runs.

The architecture is event-driven, based on event sourcing. The Core holds the majority of state, which is updated in response to events happening in the Shell. The interface between the Core and the Shell is messaged based.

The user interface layer is built natively, with modern declarative UI frameworks such as Swift UI, Jetpack Compose and React/Vue or a WASM based framework on the web.

Cloud

AWS

AWS Rust SDK

Dapr

Dapr⮳ is a portable, event-driven, serverless runtime for building distributed applications across cloud and edge.

Dapr SDK for Rust

Dapr Rust SDK example

Data and ETL

polars-badge (book) (github)

arrow-rs-badge (github) is the official Rust implementation of Apache Arrow

datafusion-badge is the Apache Arrow DataFusion SQL Query Engine.

csv-badge

Machine Learning

Are we learning yet?

linfa-badge (crates.io) (home page)

SmartCore

OpenCV (example)

Watchmaker (genetic algos in Rust)

Quant

rustquant-badge

Games

Bevy

GPU Programming

rust-gpu

Robotics

Robotics (lib.rs)

robotics.rs

Open Rust Robotics

Bonsai BT⮳ is a Rust implementation of behavior trees.

Useful tools and libraries

OpenCV

Zenoh

Windows

Windows-rs (GitHub)

Native Windows GUI

Tools

Rust Installation

Install Rust

Key Steps

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 and edit
  • Build / run the code.
cargo check
cargo build
cargo run

Crates

In Rust, a library or executable program is called a crate. Crates are compiled using the Rust compiler, rustc.

Crate Registries

The Rust community’s crate registry: crates.io

Alternative to crates.io: lib.rs

IDEs

VS Code

Rust plugin for VS Code

rust-analyzer (home page)

IntelliJ Rust

If you don’t have a JetBrains license, IntelliJ IDEA is available for free and supports IntelliJ Rust.

If you have a JetBrains license, CLion is your go-to editor for Rust in JetBrains’ IDE suite.

Cargo (package manager) and tools

The Cargo book

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 in crates.io
time = "0.1.12"
# This is equivalent to the ^0.1.12 SemVer version range.
# `cargo update -p 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 https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html

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.

# Install if needed
cargo install cargo-edit

# Add dependencies to Cargo.toml from the command line
cargo add actix-web@4.0.0

cargo-watch

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

Formatting

# Install `rustfmt` if needed
rustup component add rustfmt

cargo fmt

# Fails if code is not formatted; use in CD / CI
cargo fmt -- --check

Linting

rustup component add clippy # install if needed
cargo clippy

Mute a warning using the #[allow(clippy::lint_name)] attributes

Fix

Can automatically fix compiler warnings that have a clear way to correct the problem that’s likely what you want.

cargo fix

Code coverage

Tarpaulin

Security audit

cargo install cargo-audit
cargo audit

Unused dependencies

udeps

or (simpler) Machete

cargo install cargo-machete
cargo machete

Templates

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.

Package Layout

.
├── 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
  • 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.

Faster linking

The Rust compiler spends a lot of time in the "link" step. LLD is much faster at linking than the default Rust linker.

The default linker does a good job, but there are faster alternatives depending on the operating system you are using:

  • lld on Windows and Linux, a linker developed by the LLVM project;
  • zld on MacOS.

To speed up the linking phase you have to install the alternative linker on your machine and add this configuration file to the project:

# .cargo/config.toml
# On Windows
# ```
# cargo install -f cargo-binutils
# rustup component add llvm-tools-preview
# ```
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
[target.x86_64-pc-windows-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

# On Linux:
# - Ubuntu, `sudo apt-get install lld clang`
# - Arch, `sudo pacman -S lld clang`
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "linker=clang", "-C", "link-arg=-fuse-ld=lld"]

# On MacOS, `brew install michaeleisel/zld/zld`
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/zld"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/zld"]

cargo-binutils packages Cargo subcommands to invoke the LLVM tools shipped with the Rust toolchain.

Alternative - Mold linker

mold is up to 5× faster than lld, but with a few caveats like limited platform support and occasional stability issues. To install mold, run sudo apt-get install mold clang in Ubuntu.

You will also need to add the following to your Cargo config at .cargo/config.toml:

[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"]

Reference

Enable Fast Compiles (Bevy)

Formatting and Linting

Rustfmt

rustfmt

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

Configuration

Configuring Rustfmt

Create a rustfmt.toml in the project root folder. For example,

edition = "2021"
version = "Two"
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

Formatting attributes

For things you do not want rustfmt to mangle, use #[rustfmt::skip], #[rustfmt::skip::macros(macro_name)], or #![rustfmt::skip::attributes(custom_attribute)]

Miri Interpreter

Miri (GitHub)⮳ 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 also perform cross-interpretation for arbitrary foreign targets.

Installation

rustup +nightly component add miri
cargo clean
cargo miri test
# or
cargo miri run

Cross-compilation

Cross

cross⮳ builds your Rust project for different target operating systems and architectures. It requires rustup and Docker or Podman.

cargo install cross --git https://github.com/cross-rs/cross

# Optionally, if you have cargo-binstall, you can install via pre-built binary
cargo binstall cross

cross has the exact same CLI as Cargo but relies on Docker or Podman.

cross build --target aarch64-unknown-linux-gnu

cross test --target mips64-unknown-linux-gnuabi64

cross run --target aarch64-unknown-linux-gnu

cross Wiki

Example Cross.toml file

Rustup

rustup is a toolchain multiplexer. It installs and manages many 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.

This is similar to Python's pyenv or Node's nvm.

Key commands

rustup help

# Show the help page for a subcommand (like toolchain)
rustup toolchain help

# Open the local documentation in your browser
rustup doc

# Update to a new verion of Rust
rustup update

# Show which toolchain will be used in the current directory
rustup show

# 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 command examples

See also

Rustup documentation

Just

just is a command runner / Make replacement.

https://just.systems/

Just Programmer's Manual

Installation 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"]

# Prereqs to install Just: https://just.systems/man/en/chapter_4.html
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:

## Just: https://just.systems/man/en/chapter_1.html
RUN apk add just

Example justfile

Place it 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!"

mdBook

mdBook⮳: a utility to create modern online books from Markdown files.

cargo install mdbook
cargo install mdbook-hide  # optional plugin; many others exist
mdbook serve --open

See also

mdBook documentation

mdbook-hide

Other Tools

Rust tools

Roogle

Roogle (github)

Deployment

shuttle.rs

Tools written in Rust

My terminal became more Rusty Community

open-rs

Starship

Bacon

Bat

Exa

Essential Rust Links

Learn

In this section

Example code

RealWorld example apps

Zero To Production In Rust (book)

Zero To Production In Rust (code)

Rust Cheatsheets

cheats.rs

Rust for professionals

Comparison to other languages

Rust for C# developers

Blogs

A learning journal

LukeMathWalker/zero-to-production: Code for "Zero To Production In Rust", a book on API development using Rust

Books

Effective Rust

Programming Rust

Programming Rust, 2nd edition

The Rust Programming Language, 2nd Edition

Companies that use or contribute to Rust

Mozilla

Ferrous Systems

Embark Studios

Freiheit

Categories

Categories
cat-accessibility-badge
cat-aerospace-badge
cat-drones-badge
cat-aerospace-protocols-badge
cat-aerospace-simulation-badge
cat-space-protocols-badge
cat-unmanned-aerial-vehicles-badge
cat-algorithms-badgerand-badge
cat-api-bindings-badge
cat-asynchronous-badgeactix-web-badge
cat-authentication-badge
cat-caching-badge
cat-command-line-interface-badge
cat-command-line-utilities-badge
cat-compilers-badge
cat-compression-badge
cat-computer-vision-badge
cat-concurrency-badge
cat-config-badge
cat-cryptography-badge
cat-cryptocurrencies-badge
cat-database-implementations-badge
cat-database-badge
cat-data-structures-badge
cat-date-and-time-badge
cat-development-tools-badge
cat-cargo-plugins-badge
cat-build-utils-badge
cat-ffi-badge
cat-debugging-badge
cat-procedural-macro-helpers-badge
cat-testing-badge
cat-profiling-badge
cat-email-badge
cat-embedded-badge
cat-emulators-badge
cat-encoding-badge
cat-external-ffi-bindings-badge
cat-filesystem-badge
cat-finance-badge
cat-game-development-badge
cat-game-engines-badge
cat-games-badge
cat-graphics-badge
cat-gui-badge
cat-hardware-support-badge
cat-internationalization-badge
cat-localization-badge
cat-mathematics-badge
cat-memory-management-badge
cat-multimedia-badge
cat-video-badge
cat-images-badge
cat-audio-badge
cat-multimedia-encoding-badge
cat-network-programming-badgeactix-web-badge
cat-no-std-badge
cat-no-alloc-badge
cat-os-badge
cat-freebsd-apis-badge
cat-linux-apis-badge
cat-macos-apis-badge
cat-unix-apis-badge
cat-windows-apis-badge
cat-parser-implementations-badge
cat-parsing-badge
cat-rendering-badge
cat-rendering-engine-badge
cat-rendering-data-formats-badge
cat-rendering-graphics-api-badge
cat-rust-patterns-badge
cat-science-badge
cat-science-geo-badge
cat-neuroscience-badge
cat-robotics-badge
cat-simulation-badge
cat-template-engine-badge
cat-text-editors-badge
cat-text-processing-badge
cat-virtualization-badge
cat-visualization-badge
cat-wasm-badge
cat-web-programming-badge
cat-http-client-badgeactix-web-badge
cat-websocket-badgeactix-web-badge
cat-http-server-badge

Crates

Crates mentioned in this book, by alphabetic order.

A

actix-web-badge ansi-term-badge anyhow-badge approx-badge arrow-rs-badge async-channel-badge async-std-badge async-stream-badge async-trait-badge

B

base64-badge bitflags-badge byteorder-badge

C

cc-badge chrono-badge clap-badge config-badge confy-badge crossbeam-badge crossbeam-channel-badge crux-badge csv-badge

D

data-encoding-badge datafusion-badge dashmap-badge derive-more-badge diesel-badge dotenv-badge dotenvy-badge druid-badge

E

egui-badge embassy-badge env-logger-badge envy-badge error-chain-badge eyre-badge

F

flate2-badge futures-badge futures-executor-badge

G - K

glob-badge hyper-badge iced-rs-badge image-badge

L

lazy-static-badge linfa-badge log-badge log4rs-badge

M

memmap-badge miette-badge mime-badge mio-badge

N

nalgebra-badge ndarray-badge num-badge num-cpus-badge

O - Q

once-cell-badge paste-badge parking-lot-badge percent-encoding-badge polars-badge postage-badge postgres-badge quote-badge

R

rand-badge rand-distr-badge rayon-badge regex-badge reqwest-badge ring-badge rusqlite-badge rustquant-badge

S

same-file-badge sea-orm-badge select-badge semver-badge serde-badge serde-json-badge slint-badge smol-badge sqlx-badge std-badge syslog-badge

T

tar-badge tauri-badge tempdir-badge thiserror-badge threadpool-badge tokio-badge tokio-graceful-shutdown-badge toml-badge tower-badge tower-http-badge tonic-badge tracing-badge tracing-subscriber-badge

U

url-badge unicode-segmentation-badge

V - Z

walkdir-badge watt-badge yew-badge

Thanks

This reference guide is inspired from online websites, blogs, and documentation, including: