Rust

Rust basics

Rust toolchain is tightly integrated with documentation and development with cargo.

Rust features

Algebraic Type System

A trait defines the functionality a particular type has and can share with other types. Struct and Enum are user defined types in Rust. The algebraic type system allows modeling with types and traits that makes code harder to misuse. Think about valid states when modeling.

1
2
3
4
enum IpAddrKind {
    V4,
    V6,
}
1
2
3
4
5
6
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

Macros

Macros are Rust code that writes other Rust code (metaprogramming). Allows

Macros are created using the macro_rules! macro.

1
2
3
4
5
6
7
8
9
10
11
12
macro_rules! say_hello {
    // `()` indicates that the macro takes no argument.
    () => {
        // The macro will expand into the contents of this block.
        println!("Hello!")
    };
}

fn main() {
    // This call will expand into `println!("Hello!")`
    say_hello!()
}

Perfect backwards compatibility

Crater compiles and tests every crate. It is used to verify that new versions of Rust are compatible with all crates.

Ownership

Compiler rustc checks the rules governing memory ownership.

Function std::mem::drop takes ownership of a value and the value is dropped automatically before it returns. The whole function definition is

1
pub fn drop<T>(_x: T) {}

Memory stack is last in first out (LIFO) and faster than the heap. Data with an unknown size at compile time or a size that might change must be stored on the heap.

No garbage collection

No garbage collection means no "stop the world" pauses in code execution.

Variables have lifetimes

Let the compiler teach usage of lifetimes.

Unsafe system

Allows operations that Rust can not guarantee the safety of and limits where to search for memory bugs.

Rust development, publishing and distribution

Install Rust and cargo

Maintenance and common operations

1
rustup update
1
cargo clean --help

Remove a crate with

1
cargo uninstall

Toolchains

1
2
3
rustup toolchain list --help
rustup component list -h
rustup component add rust-analyzer

Other local documentation - books etc.

1
2
3
4
5
rustup doc --path --toolchain stable --clippy
rustup doc --toolchain beta fn
rustup doc --reference
rustup man rustc
rustc --explain E0425

Packages

Rust code is packaged as a collection of crates. It's possible to create private package registries.

1
cargo search mask

Cargo honor locked versions, do a reproducible all binaries install.

1
cargo install --bins --locked mask
1
cargo install --list

Show package information with cargo info

Fearless Rust

Write the dumbest version first, optimise later (for multithreading - tokio, performance, optimal structures, cache etc). Clone data, don't bother with a reference (ignore lifetime issues). Waste RAM to get it working, worry later. Improvements are easy once you got it to compile the first time. Small changes easier.

Think of speed later, use for safety and type system.

1
cargo clippy --fix -- -W clippy::pedantic -W clippy::nursery -W clippy::unwrap_used -W clippy::expect_used

Rust By Example

1
rustup doc --rust-by-example

Intro

1
cargo install --locked rustlings manrs

Rustlings exercises and the Rust book (The Rust Programming Language, rustup doc --book) are interlinked, Rust By Example is more loosely associated with them.

manrs allows reading the docs without a web browser. Show only examples of I/O

1
manrs -e std::io

Language topics

Rustup toolchain

A toolchain may become outdated when crates have become a component between updates.

1
2
rustup toolchain uninstall stable
rustup toolchain install stable

Profile is a grouping of components.

1
2
rustup show profile
rustup install --profile minimal beta

The default target architecture for cargo and rustc is the host toolchain's platform.

Toolchain may be overridden on a project basis.

1
2
3
4
5
6
# project_directory/rust-toolchain.toml
[toolchain]
channel = "nightly-2020-07-10"
components = [ "rustfmt", "rustc-dev" ]
targets = [ "arm-linux-androideabi", "thumbv2-none-eabi" ]
profile = "minimal"

More cargo, project dependency updates

The yank command removes a previously published crate's version from the server's index. This command does not delete any data, and the crate will still be available for download via the registry's download link.

Cargo will not use a yanked version for any new project or checkout without a pre-existing lockfile, and will generate an error if there are no longer any compatible versions for your crate.

Suppose bitstream-io version used depends on a yanked crate (deprecated core2), requires a version update.

1
2
cargo update bitstream-io
cargo test

Documentation comments

Using match instead of if makes compiler errors more helpful

if can do anything but match is type safe, compiler can determine what cases may be missing.

Box

<T> - Type

A pointer type Box<T> for heap allocation.

1
rustup doc std::boxed

Option type

1
rustup doc std::option::Option

Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not.

There are no "null" references. Instead, Rust has optional pointers, like the optional owned box, Option<Box<T>>.

Infallible and ! never type

core::convert::Infallible is the error type for errors that can never happen, hence the result is always Ok. Deprecation in favour of ! is planned.

! is a type with no values, representing the result of computations that never complete. Currently unstable.

1
2
3
fn foo() -> ! {
    panic!("This call never returns.");
}

Borrow checker

Borrow checker rules.

  1. data has one owner
  2. data may have multiple readers or one writer

Trying to use data that has been passed for writing or moved results in error use of moved value.

Threads and borrowing references

Avoid immortal (static) references when writing asynchronous code. Allows borrowing references.

Wait for threads to complete, this happens at the end of a scope. Concurrency is a major goal of Rust, along with safety.

1
2
3
# Cargo.toml
[dependencies]
minreq = { version >= "2.14.1" }
1
2
3
4
5
6
7
8
9
// scope blocks until threads are done, therefore
// rustc has a guarantee that all threads will finish (join).

fn expensive_scope(url: &String) {
  std::thread::scope(|s| {
    s.spawn(|| minreq::get(url));
  })
  dbg!("url: {}", url);
}

Rust parallel asynchronous

Better not to use async unless threads is not enough.

tokio - most widely used async library, for network functions (need to wait for I/O). Muddies code, better to do without in the beginning. Consider a scoped tokio runtime.

std::sync::Arc - Atomically Reference Counted, thread-safe reference-counting pointer.

smol is a small and fast async runtime.

rayon - converts iterators into parallel iterators. Rayon guarantees you that using Rayon APIs will not introduce data races.

Rust other language tools

sqlx

Async SQL toolkit for Rust, with compile time SQL validation.

poem-openapi

Uses procedural macros to generate a lot of boilerplate code so your code is a clear and clean implementation of needs. Complies with the OpenAPIv3 specification.

serde

Serialize, deserialize data types.

rstml

Backend html.

yew

Frontend html.

reqwest

Convenient, higher-level HTTP client.

tracing

Async native (tokio) logging.

color_eyre

Colourful, consistent, well formatted error reports.

chrono

Date and time library.

irust

Interactive Rust read-eval-print-loop (REPL), debug, asm inspection.

1
2
:help
:quit

Rust (mobile) app development

Tauri

Tauri uses web technologies, that means that virtually any frontend framework is compatible with Tauri.

Rust web development

Rust game development

Godot has Rust bindings.