Post

What is that thing in Rust called?

Rust vocabulary that is impossible to google - a reference for those mysterious syntax elements and concepts.

What is that thing in Rust called?

A few years back, I heard D.H.A.R.N.T.Z. by Jazz Emu. Unfortunately, the song is about homophones, which makes it insanely hard to find online if you forgot the artists name and the exact spelling. I searched for months, trying various misremembered spellings “darnts”, “dantz”, “darnz”, etc. Eventually, desparate and hungry for funk, I went on Reddit and made a post asking for help.

No answers, just the bot telling me the subreddit rules. A few days later, somebody sends some suggestions, but they were unfortunately wrong.

It took 6 months of waiting, until a kind stranger came along, and finally granted me the answer, which led me back to Jazz Emu, and to the whole repertoire of amazing music which I might have otherwise missed (btw, LLMs do not help at all, had no luck with them with what I remebered of the song when I tried before writing this post).

Sometimes, programming can be a bit like that as well. You see some syntax or concept, but you don’t know what it’s called, and searching for it is impossible. This post is a reference for those mysterious Rust syntax elements and concepts that are hard to google, or remember.

Syntax Elements

“Turbofish” = ::<>

The thing you use for explicit generic type parameters:

1
2
let numbers = Vec::<i32>::new();
let parsed = "42".parse::<i32>().unwrap();

“Fat Arrow” = =>

Used in match arms and closures:

1
2
3
4
5
6
match value {
    Some(x) => println!("{}", x),
    None => println!("Nothing"),
}

let closure = |x| => x * 2; // also closures, can be hard to remember the word

“Thin Arrow” = ->

Function return type annotation:

1
2
3
fn add(a: i32, b: i32) -> i32 {
    a + b
}

“Double Colon” = ::

Path separator for modules, associated functions, and types:

1
2
std::collections::HashMap::new()
String::from("hello")

Ownership & Borrowing

“Borrow Checker”

The thing that makes you hate Rust at first, then love it:

1
2
3
let s1 = String::from("hello");
let s2 = &s1; // borrowing
// let s3 = s1; // error: value used after move

“Lifetime Elision”

Why you don’t always need to write lifetime parameters:

1
2
3
4
5
6
7
8
9
// This works without explicit lifetimes
fn first_word(s: &str) -> &str {
    s.split_whitespace().next().unwrap_or("")
}

// But this is what the compiler infers:
fn first_word_explicit<'a>(s: &'a str) -> &'a str {
    s.split_whitespace().next().unwrap_or("")
}

“RAII” = Resource Acquisition Is Initialization

Why Drop gets called automatically:

1
2
3
4
{
    let file = File::open("data.txt")?; // acquired
    // use file
} // automatically dropped/closed here

Pattern Matching

“Irrefutable Patterns”

Patterns that always match:

1
2
let (x, y) = (1, 2); // always works
let Some(value) = maybe_value; // refutable - might panic!

“Match Guards”

Extra conditions in match arms:

1
2
3
4
5
match number {
    x if x > 0 => "positive",
    x if x < 0 => "negative", 
    _ => "zero",
}

“Destructuring”

Taking apart structs/enums in patterns:

1
2
3
4
struct Point { x: i32, y: i32 }

let p = Point { x: 0, y: 7 };
let Point { x, y } = p; // destructuring

Traits & Generics

“Blanket Implementations”

Implementing a trait for all types that satisfy certain bounds:

1
2
3
4
5
impl<T: Display> ToString for T {
    fn to_string(&self) -> String {
        // implementation
    }
}

“Associated Types” vs “Generic Parameters”

1
2
3
4
5
6
7
8
9
10
// Associated type - one concrete type per implementation
trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

// Generic parameter - can be used with multiple types
trait From<T> {
    fn from(value: T) -> Self;
}

“Higher-Ranked Trait Bounds” (HRTB)

The for<'a> syntax:

1
2
3
4
5
6
fn call_with_ref<F>(f: F) 
where 
    F: for<'a> Fn(&'a str) -> &'a str
{
    // f can work with any lifetime
}

Macros

“Declarative Macros” = macro_rules!

Pattern-based code generation:

1
2
3
4
5
6
7
8
9
10
11
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

“Procedural Macros”

The three types that run at compile time:

  • Derive macros: #[derive(Debug)]
  • Attribute macros: #[my_attribute]
  • Function-like macros: my_macro!()

Memory Layout

“Zero-Cost Abstractions”

High-level features that compile to the same assembly as hand-written low-level code:

1
2
3
4
5
6
7
8
9
10
11
12
13
// This iterator chain...
let sum: i32 = (0..1_000_000)
    .filter(|x| x % 2 == 0)
    .map(|x| x * x)
    .sum();

// ...compiles to roughly the same thing as:
let mut sum = 0;
for i in 0..1_000_000 {
    if i % 2 == 0 {
        sum += i * i;
    }
}

“Fat Pointers”

Pointers that carry extra metadata:

1
2
let slice: &[i32] = &[1, 2, 3]; // pointer + length
let trait_obj: &dyn Display = &42; // pointer + vtable

Error Handling

“Railway-Oriented Programming”

Chaining operations that might fail:

1
2
3
4
let result = read_file("data.txt")?
    .parse::<i32>()?
    .checked_mul(2)
    .ok_or("overflow")?;

“Error Propagation” = ? operator

The thing that replaced .unwrap() everywhere:

1
2
3
4
5
6
7
8
// Instead of:
match some_operation() {
    Ok(value) => value,
    Err(e) => return Err(e),
}

// Just write:
some_operation()?

Async/Await

“Zero-Cost Futures”

Async functions compile to state machines:

1
2
3
4
async fn fetch_data() -> Result<String, Error> {
    let response = http_client.get("url").await?;
    Ok(response.text().await?)
}

“Pin” and “Unpin”

The thing that makes self-referential futures work (and confuses everyone):

1
2
3
// Most types are Unpin (can be moved safely)
// Some futures are !Unpin (contain self-references)
let pinned = Box::pin(some_future);

Tools That Are Better Than The Obvious Choice

cargo-expand

Better than trying to read macro output:

1
2
cargo install cargo-expand
cargo expand --lib my_module

cargo-watch

Better than manually running cargo commands:

1
2
cargo install cargo-watch  
cargo watch -x test

cargo-llvm-cov

What Rust coverage should be:

1
2
cargo install cargo-llvm-cov
cargo llvm-cov

Know any other impossible-to-google Rust terms? Let me know and I’ll add them!

Pro tip: When you see mysterious syntax, try searching for “rust [exact symbols]” - the Rust community is pretty good about using the actual symbols in documentation.

This post is licensed under CC BY 4.0 by the author.