Rust

43. Vec::extract_if — Remove Elements and Keep Them

Ever needed to split a Vec into two groups — the ones you keep and the ones you remove? retain discards the removed items. Now there’s a better way.

Vec::extract_if (stable since Rust 1.87) removes elements matching a predicate and hands them back as an iterator — in a single pass.

The old way — two passes, logic must stay in sync

1
2
3
4
5
6
7
8
9
let mut numbers = vec![1, 2, 3, 4, 5, 6];

// Collect the evens first…
let evens: Vec<i32> = numbers.iter().filter(|&&x| x % 2 == 0).copied().collect();
// …then remove them (predicate must match exactly)
numbers.retain(|&x| x % 2 != 0);

assert_eq!(numbers, [1, 3, 5]);
assert_eq!(evens,   [2, 4, 6]);

The filter and the retain predicates must be inverses of each other — easy to mistype, and you touch the data twice.

The new way — one pass, one predicate

1
2
3
4
5
6
let mut numbers = vec![1, 2, 3, 4, 5, 6];

let evens: Vec<i32> = numbers.extract_if(.., |&mut x| x % 2 == 0).collect();

assert_eq!(numbers, [1, 3, 5]);
assert_eq!(evens,   [2, 4, 6]);

extract_if walks the Vec, removes every element where the closure returns true, and yields it. The .. is a range — you can narrow it to only consider a slice of the vector.

Real-world example: draining a work queue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#[derive(Debug)]
struct Job { id: u32, priority: u8 }

let mut queue = vec![
    Job { id: 1, priority: 3 },
    Job { id: 2, priority: 9 },
    Job { id: 3, priority: 1 },
    Job { id: 4, priority: 8 },
];

// Pull out all high-priority jobs for immediate processing
let urgent: Vec<Job> = queue.extract_if(.., |j| j.priority >= 8).collect();

assert_eq!(urgent.len(), 2);  // jobs 2 and 4
assert_eq!(queue.len(),  2);  // jobs 1 and 3 remain

HashMap and HashSet also gained extract_if in Rust 1.88.

Note: The closure takes &mut T, so you can even mutate elements mid-extraction before deciding whether to remove them.

#042 Mar 2026

42. array::from_fn — Build Arrays from a Function

Need a fixed-size array where each element depends on its index? Skip the vec![..].try_into().unwrap() dance — std::array::from_fn builds it in place with zero allocation.

The problem

Rust arrays [T; N] have a fixed size known at compile time, but initializing them with computed values used to be awkward:

1
2
3
4
5
6
// Clunky: build a Vec, then convert
let squares: [u64; 5] = (0..5)
    .map(|i| i * i)
    .collect::<Vec<_>>()
    .try_into()
    .unwrap();

That allocates a Vec on the heap just to get a stack array. For Copy types you could use [0u64; 5] and a loop, but that doesn’t work for non-Copy types and is verbose either way.

The fix: array::from_fn

1
2
let squares: [u64; 5] = std::array::from_fn(|i| (i as u64) * (i as u64));
assert_eq!(squares, [0, 1, 4, 9, 16]);

The closure receives the index (0..N) and returns the element. No heap allocation, no unwrapping — just a clean array on the stack.

Non-Copy types? No problem

from_fn shines when your elements don’t implement Copy:

1
2
3
let labels: [String; 4] = std::array::from_fn(|i| format!("item_{i}"));
assert_eq!(labels[0], "item_0");
assert_eq!(labels[3], "item_3");

Try doing that with [String::new(); 4] — the compiler won’t let you because String isn’t Copy.

Stateful initialization

The closure can capture mutable state. Elements are produced left to right, index 0 first:

1
2
3
4
5
6
7
let mut acc = 1u64;
let powers_of_two: [u64; 6] = std::array::from_fn(|_| {
    let val = acc;
    acc *= 2;
    val
});
assert_eq!(powers_of_two, [1, 2, 4, 8, 16, 32]);

A practical example: lookup tables

Build a compile-time-friendly lookup table for ASCII case conversion offsets:

1
2
3
4
5
6
let is_upper: [bool; 128] = std::array::from_fn(|i| {
    (i as u8).is_ascii_uppercase()
});
assert!(is_upper[b'A' as usize]);
assert!(!is_upper[b'a' as usize]);
assert!(!is_upper[b'0' as usize]);

Why it matters

std::array::from_fn has been stable since Rust 1.63. It avoids heap allocation, works with any type (no Copy or Default bound), and keeps your code readable. Anytime you reach for Vec just to build a fixed-size array — stop, and use from_fn instead.

#041 Mar 2026

41. Async Closures — Pass Async Code Like Any Other Closure

Accepting an async callback used to mean a tangle of Fn(T) -> Fut where Fut: Future. Rust 1.85 stabilizes async closures — write async |x| { ... } and accept them with impl async Fn(T) -> U.

The old workaround

Before Rust 1.85, accepting an async function as a parameter meant spelling out the future type explicitly:

1
2
3
4
5
6
7
async fn run<F, Fut>(f: F, x: i32) -> i32
where
    F: Fn(i32) -> Fut,
    Fut: std::future::Future<Output = i32>,
{
    f(x).await
}

It compiles, but the signature is noisy. It gets worse once you need FnMut, higher-ranked lifetimes, or closures that borrow from their captures.

The new way

1
2
3
4
5
6
7
async fn run(f: impl AsyncFn(i32) -> i32, x: i32) -> i32 {
    f(x).await
}

let double = async |x: i32| x * 2;
let result = run(double, 21).await;
assert_eq!(result, 42);

async |...| { ... } is the syntax for an async closure. Use AsyncFn(T) -> U bounds at call sites — AsyncFn, AsyncFnMut, and AsyncFnOnce mirror the regular Fn family and were stabilized alongside async closures in Rust 1.85.

Capturing state works naturally

Async closures capture their environment exactly like regular closures:

1
2
3
4
let base = 10_i32;
let adder = async |x: i32| base + x;

assert_eq!(adder(32).await, 42);

No extra boxing or lifetime gymnastics — the closure borrows base just as a sync closure would.

Apply it twice

1
2
3
4
5
6
async fn apply_twice(f: impl AsyncFn(i32) -> i32, x: i32) -> i32 {
    f(f(x).await).await
}

let double = async |x: i32| x * 2;
assert_eq!(apply_twice(double, 5).await, 20); // 5 → 10 → 20

Why it matters

The real payoff is in generic async APIs — retry helpers, middleware, event hooks — anywhere you’d pass a callback. Instead of Pin<Box<dyn Future>> boilerplate you get a clean bound:

1
2
3
4
5
6
7
8
async fn retry(op: impl AsyncFn() -> Result<i32, String>, tries: u32) -> Option<i32> {
    for _ in 0..tries {
        if let Ok(val) = op().await {
            return Some(val);
        }
    }
    None
}

Async closures are available in Rust 1.85+ (stable since February 2025). Make sure your crate uses edition = "2021" or later.

40. Scoped Threads — Borrow Across Threads Without Arc

Need to share stack data with spawned threads? std::thread::scope lets you borrow local variables across threads — no Arc, no .clone().

The problem

With std::thread::spawn, you can’t borrow local data because the thread might outlive the data:

1
2
3
4
5
6
7
let data = vec![1, 2, 3];

// This won't compile — `data` might be dropped
// while the thread is still running
// std::thread::spawn(|| {
//     println!("{:?}", data);
// });

The classic workaround is wrapping everything in Arc:

1
2
3
4
5
6
7
8
9
use std::sync::Arc;

let data = Arc::new(vec![1, 2, 3]);
let data_clone = Arc::clone(&data);

let handle = std::thread::spawn(move || {
    println!("{:?}", data_clone);
});
handle.join().unwrap();

It works, but it’s noisy — especially when you just want to read some data in parallel.

The fix: std::thread::scope

Scoped threads guarantee that all spawned threads finish before the scope exits, so borrowing is safe:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
let data = vec![1, 2, 3];
let mut results = vec![];

std::thread::scope(|s| {
    s.spawn(|| {
        // Borrowing `data` directly — no Arc needed
        println!("Thread sees: {:?}", data);
    });

    s.spawn(|| {
        let sum: i32 = data.iter().sum();
        println!("Sum: {sum}");
    });
});

// All threads have joined here — guaranteed
println!("Done! data is still ours: {:?}", data);

Mutable access works too

Since the scope enforces proper lifetimes, you can even have one thread mutably borrow something:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let mut counts = [0u32; 3];

std::thread::scope(|s| {
    for (i, count) in counts.iter_mut().enumerate() {
        s.spawn(move || {
            *count = (i as u32 + 1) * 10;
        });
    }
});

assert_eq!(counts, [10, 20, 30]);

Each thread gets exclusive access to its own element — the borrow checker is happy, no Mutex required.

When to reach for scoped threads

Use std::thread::scope when you need parallel work on local data and don’t want the overhead or ceremony of Arc/Mutex. It’s perfect for fork-join parallelism: spin up threads, borrow what you need, collect results when they’re done.

39. Trait Upcasting — Cast dyn Trait to dyn Supertrait

Since Rust 1.86, you can upcast a trait object to its supertrait — no workarounds needed.

The problem

Imagine you have a trait hierarchy:

1
2
3
4
5
use std::any::Any;

trait Animal: Any {
    fn name(&self) -> &str;
}

Before Rust 1.86, if you had a &dyn Animal, you couldn’t simply cast it to &dyn Any. You’d have to add an explicit method to your trait:

1
2
3
4
5
// The old workaround — adding a method just to upcast
trait Animal: Any {
    fn name(&self) -> &str;
    fn as_any(&self) -> &dyn Any;
}

This was boilerplate that every trait hierarchy had to carry around.

The fix: trait upcasting

Now you can coerce a trait object directly to any of its supertraits:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use std::any::Any;

trait Animal: Any {
    fn name(&self) -> &str;
}

struct Dog;

impl Animal for Dog {
    fn name(&self) -> &str {
        "Rex"
    }
}

fn print_if_dog(animal: &dyn Animal) {
    // Upcast to &dyn Any — just works!
    let any: &dyn Any = animal;

    if let Some(dog) = any.downcast_ref::<Dog>() {
        println!("Good boy, {}!", dog.name());
    } else {
        println!("Not a dog.");
    }
}

fn main() {
    let dog = Dog;
    print_if_dog(&dog);
}

The key line is let any: &dyn Any = animal; — this coercion from &dyn Animal to &dyn Any used to be a compiler error and now just works.

It works with any supertrait chain

Upcasting isn’t limited to Any. It works for any supertrait relationship:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
trait Drawable {
    fn draw(&self);
}

trait Widget: Drawable {
    fn click(&self);
}

fn draw_it(widget: &dyn Widget) {
    // Coerce &dyn Widget → &dyn Drawable
    let drawable: &dyn Drawable = widget;
    drawable.draw();
}

This makes trait object hierarchies much more ergonomic. No more as_drawable() helper methods cluttering your traits.

38. #[must_use] — Never Ignore What Matters

Rust’s #[must_use] attribute turns silent bugs into compile-time warnings — making sure important return values never get accidentally ignored.

The Problem: Silently Ignoring Results

Here’s a classic bug that can haunt any codebase:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
fn remove_expired_tokens(tokens: &mut Vec<String>) -> usize {
    let before = tokens.len();
    tokens.retain(|t| !t.starts_with("exp_"));
    before - tokens.len()
}

fn main() {
    let mut tokens = vec![
        "exp_abc".to_string(),
        "valid_xyz".to_string(),
        "exp_def".to_string(),
    ];

    // Bug: we call the function but ignore the count!
    remove_expired_tokens(&mut tokens);

    // No warning, no error — the return value just vanishes
}

The function works fine, but the caller threw away useful information without even a whisper from the compiler.

The Fix: #[must_use]

Add #[must_use] to the function and the compiler has your back:

1
2
3
4
5
6
#[must_use = "returns the number of removed tokens"]
fn remove_expired_tokens(tokens: &mut Vec<String>) -> usize {
    let before = tokens.len();
    tokens.retain(|t| !t.starts_with("exp_"));
    before - tokens.len()
}

Now if someone calls remove_expired_tokens(&mut tokens); without using the result, the compiler emits:

1
2
3
4
warning: unused return value of `remove_expired_tokens` that must be used
  --> src/main.rs:14:5
   |
   = note: returns the number of removed tokens

Works on Types Too

#[must_use] isn’t just for functions — it shines on types:

1
2
3
4
5
#[must_use = "this Result may contain an error that should be handled"]
enum DatabaseResult<T> {
    Ok(T),
    Err(String),
}

This is exactly why calling .map() on an iterator without collecting produces a warning — Map is marked #[must_use] in std.

Already in the Standard Library

Rust’s standard library uses #[must_use] extensively. Result, Option, MutexGuard, and many iterator adapters are all marked with it. That’s why you get a warning for:

1
vec![1, 2, 3].iter().map(|x| x * 2);  // warning: unused `Map`

The iterator does nothing until consumed — and #[must_use] makes sure you don’t forget.

Quick Rules

Use #[must_use] when:

  • A function returns a Result or error indicator — callers should handle failures
  • A function is pure (no side effects) — ignoring the return means the call was pointless
  • A type is lazy (like iterators) — it does nothing until consumed
  • The return value carries critical information the caller likely needs

The custom message string is optional but highly recommended — it tells the developer why they shouldn’t ignore the value.

37. Option::zip

Need to combine two Option values into a pair? Option::zip merges them into a single Option<(A, B)> — if either is None, you get None back.

The problem

You have two optional values and need both to proceed. The classic approach uses nested matching:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
let name: Option<&str> = Some("Alice");
let age: Option<u32> = Some(30);

// Nested match — gets unwieldy fast
let greeting = match name {
    Some(n) => match age {
        Some(a) => Some(format!("{n} is {a} years old")),
        None => None,
    },
    None => None,
};

assert_eq!(greeting, Some("Alice is 30 years old".to_string()));

The fix: Option::zip

Zip collapses two Options into one tuple:

1
2
3
4
5
6
let name: Option<&str> = Some("Alice");
let age: Option<u32> = Some(30);

let greeting = name.zip(age).map(|(n, a)| format!("{n} is {a} years old"));

assert_eq!(greeting, Some("Alice is 30 years old".to_string()));

One line instead of six. If either value is None, zip short-circuits to None:

1
2
3
4
let name: Option<&str> = Some("Alice");
let age: Option<u32> = None;

assert_eq!(name.zip(age), None);

Bonus: zip with and_then

You can chain zip into more complex pipelines:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fn lookup_user(id: u32) -> Option<String> {
    if id == 1 { Some("Alice".to_string()) } else { None }
}

fn lookup_role(id: u32) -> Option<String> {
    if id == 1 { Some("Admin".to_string()) } else { None }
}

let result = lookup_user(1)
    .zip(lookup_role(1))
    .map(|(user, role)| format!("{user} ({role})"));

assert_eq!(result, Some("Alice (Admin)".to_string()));

Option::zip is stable since Rust 1.46 and works anywhere you need both-or-nothing semantics without the nesting.

36. Cow<str> — Clone on Write

Stop cloning strings “just in case” — Cow<str> lets you borrow when you can and clone only when you must.

The problem

You’re writing a function that sometimes needs to modify a string and sometimes doesn’t. The easy fix? Clone every time:

1
2
3
4
5
6
7
fn ensure_greeting(name: &str) -> String {
    if name.starts_with("Hello") {
        name.to_string() // unnecessary clone!
    } else {
        format!("Hello, {name}!")
    }
}

This works, but that first branch allocates a brand-new String even though name is already perfect as-is. In a hot loop, those wasted allocations add up.

Enter Cow<str>

Cow stands for Clone on Write. It holds either a borrowed reference or an owned value, and only clones when you actually need to mutate or take ownership:

1
2
3
4
5
6
7
8
9
use std::borrow::Cow;

fn ensure_greeting(name: &str) -> Cow<str> {
    if name.starts_with("Hello") {
        Cow::Borrowed(name) // zero-cost: just wraps the reference
    } else {
        Cow::Owned(format!("Hello, {name}!"))
    }
}

Now the happy path (name already starts with “Hello”) does zero allocation. The caller gets a Cow<str> that derefs to &str transparently — most code won’t even notice the difference.

Using Cow values

Because Cow<str> implements Deref<Target = str>, you can use it anywhere a &str is expected:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::borrow::Cow;

fn ensure_greeting(name: &str) -> Cow<str> {
    if name.starts_with("Hello") {
        Cow::Borrowed(name)
    } else {
        Cow::Owned(format!("Hello, {name}!"))
    }
}

fn main() {
    let greeting = ensure_greeting("Hello, world!");
    assert_eq!(&*greeting, "Hello, world!");

    // Call &str methods directly on Cow
    assert!(greeting.contains("world"));

    // Only clone into String when you truly need ownership
    let _owned: String = greeting.into_owned();

    let greeting2 = ensure_greeting("Rust");
    assert_eq!(&*greeting2, "Hello, Rust!");
}

When to reach for Cow

Cow shines in these situations:

  • Conditional transformations — functions that modify input only sometimes (normalization, trimming, escaping)
  • Config/lookup values — return a static default or a dynamically built string
  • Parser outputs — most tokens are slices of the input, but some need unescaping

The Cow type works with any ToOwned pair, not just strings. You can use Cow<[u8]>, Cow<Path>, or Cow<[T]> the same way.

Quick reference

OperationCost
Cow::Borrowed(s)Free — wraps a reference
Cow::Owned(s)Whatever creating the owned value costs
*cow (deref)Free
cow.into_owned()Free if already owned, clones if borrowed
cow.to_mut()Clones if borrowed, then gives &mut access

35. LazyLock

Still pulling in lazy_static or once_cell just for a lazy global? std::sync::LazyLock does the same thing — zero dependencies.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
use std::sync::LazyLock;

static CONFIG: LazyLock<Vec<String>> = LazyLock::new(|| {
    // Imagine this reads from a file or env
    vec!["debug".to_string(), "verbose".to_string()]
});

fn main() {
    // CONFIG is initialized on first access
    println!("flags: {:?}", *CONFIG);
    assert_eq!(CONFIG.len(), 2);
}

LazyLock was stabilized in Rust 1.80 as the std replacement for once_cell::sync::Lazy and lazy_static!. It initializes the value exactly once on first access, is Sync by default, and works in static items without macros.

For single-threaded or non-static use, there’s also LazyCell — same idea but without the synchronization overhead:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use std::cell::LazyCell;

fn main() {
    let greeting = LazyCell::new(|| {
        println!("computing...");
        "Hello, Rust!".to_uppercase()
    });

    println!("before access");
    // "computing..." prints here, on first deref
    assert_eq!(*greeting, "HELLO, RUST!");
    // second access — no recomputation
    assert_eq!(*greeting, "HELLO, RUST!");
}

The output is:

1
2
before access
computing...

The closure runs lazily on first Deref, and the result is cached for all subsequent accesses. No unwrap(), no Mutex, no external crates — just clean lazy initialization from std.

34. array_windows

Need to look at consecutive pairs (or triples) in a slice? Stop manually indexing — array_windows gives you fixed-size windows as arrays, not slices.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
let temps = [18.0, 21.5, 19.0, 23.0, 22.5];

// Before: manual indexing 😬
for i in 0..temps.len() - 1 {
    let diff = temps[i + 1] - temps[i];
    println!({diff:+.1}");
}

// After: array_windows ✨
for [prev, next] in temps.array_windows() {
    let diff = next - prev;
    println!({diff:+.1}");
}

Stabilized in Rust 1.94, array_windows works like .windows(n) but the window size is a const generic — so you get &[T; N] instead of &[T]. That means you can destructure directly in the pattern.

It’s great for detecting trends, computing deltas, or validating sequences:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let readings = [3, 7, 2, 9, 1, 8];

let all_increasing = readings
    .array_windows()
    .all(|[a, b]| b > a);

assert!(!all_increasing);

// Works with triples too
let has_valley = readings
    .array_windows()
    .any(|[a, b, c]| b < a && b < c);

assert!(has_valley); // 2 is a valley between 7 and 9

No bounds checks, no .try_into().unwrap() dance. Just clean pattern matching on fixed-size windows.

#033 Mar 2026

33. std::mem::take

Ever tried to move a value out of a &mut reference? The borrow checker won’t let you — but std::mem::take will. It swaps the value out and leaves Default::default() in its place.

1
2
3
4
5
6
7
use std::mem;

let mut name = String::from("Ferris");
let taken = mem::take(&mut name);

assert_eq!(taken, "Ferris");
assert_eq!(name, ""); // left with String::default()

This is especially useful when working with enum state machines where you need to consume the current state:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
use std::mem;

enum State {
    Running(String),
    Stopped,
}

impl Default for State {
    fn default() -> Self { State::Stopped }
}

fn reset(state: &mut State) -> Option<String> {
    match mem::take(state) {
        State::Running(data) => Some(data),
        State::Stopped => None,
    }
}

Without mem::take, you’d need .clone() or unsafe gymnastics to get the value out. See also mem::replace for when you want to specify what to leave behind instead of using Default.

#032 Mar 2026

32. iter::successors

Need to generate a sequence where each element depends on the previous one? std::iter::successors turns any “next from previous” logic into a lazy iterator.

1
2
3
4
5
6
7
8
use std::iter::successors;

// Powers of 10 that fit in a u32
let powers: Vec<u32> = successors(Some(1u32), |&n| n.checked_mul(10))
    .collect();

// [1, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000, 1_000_000_000]
assert_eq!(powers.len(), 10);

You give it a starting value and a closure that computes the next element from a reference to the current one. Return None to stop — here checked_mul naturally returns None on overflow, so the iterator terminates on its own.

It works great for any recurrence:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use std::iter::successors;

// Collatz sequence starting from 12
let collatz: Vec<u64> = successors(Some(12u64), |&n| match n {
    1 => None,
    n if n % 2 == 0 => Some(n / 2),
    n => Some(3 * n + 1),
}).collect();

assert_eq!(collatz, vec![12, 6, 3, 10, 5, 16, 8, 4, 2, 1]);

Think of it as unfold for when your state is the yielded value. Simple, lazy, and zero-allocation until you collect.

31. HashMap's entry API

Want to insert a value into a HashMap only if the key doesn’t exist yet? Skip the double lookup — use the entry API.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use std::collections::HashMap;

let mut scores: HashMap<&str, Vec<u32>> = HashMap::new();

// Instead of checking .contains_key() then inserting:
scores.entry("alice")
    .or_insert_with(Vec::new)
    .push(100);

scores.entry("alice")
    .or_insert_with(Vec::new)
    .push(200);

assert_eq!(scores["alice"], vec![100, 200]);

The entry API returns an Entry enum — either Occupied or Vacant. The convenience methods make common patterns a one-liner:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use std::collections::HashMap;

let mut word_count: HashMap<&str, usize> = HashMap::new();
let words = ["hello", "world", "hello", "rust", "hello"];

for word in words {
    *word_count.entry(word).or_insert(0) += 1;
}

// hello => 3, world => 1, rust => 1

or_insert(val) inserts a default, or_insert_with(|| val) lazily computes it, and or_default() uses the type’s Default. All three return a mutable reference to the value, so you can update in place.

#030 Mar 2026

30. dbg! macro

Still using println! for quick debugging? Try dbg! instead — it prints the expression, its value, and the file/line number to stderr. And it returns the value, so you can wrap it around anything.

1
2
3
4
5
6
let a = 2;
let b = dbg!(a * 2) + 1; // prints: [src/main.rs:3] a * 2 = 4
assert_eq!(b, 5);

// works with multiple values too
dbg!(a, b, a + b); // prints each as a tuple

Unlike println!, dbg! takes ownership (or copies for Copy types). If you need to keep the value, pass a reference:

1
2
3
let name = String::from("Ferris");
dbg!(&name); // borrows, doesn't move
println!("{name}"); // still works!

Bonus: dbg! works the same in release builds, and outputs to stderr so it won’t pollute your stdout.

29. Let chains

Tired of deeply nested if let blocks? Rust 2024 edition brings let chains — chain multiple let patterns with && in a single if expression.

1
2
3
4
5
6
7
8
// Before: nested and hard to read
if let Some(a) = opt_a {
    if let Some(b) = opt_b {
        if a > 0 {
            println!("{a} + {b} = {}", a + b);
        }
    }
}

With let chains, flatten the whole thing:

1
2
3
4
5
6
if let Some(a) = opt_a
    && let Some(b) = opt_b
    && a > 0
{
    println!("{a} + {b} = {}", a + b);
}

Works with while too!

1
2
3
4
5
6
7
8
9
let mut iter = vec![Some(1), Some(2), None, Some(4)].into_iter();

while let Some(inner) = iter.next()
    && let Some(val) = inner
{
    println!("got: {val}");
}
// prints: got: 1, got: 2
// stops at None — the inner let fails

You can mix boolean expressions and let bindings freely. Each && can be either a regular condition or a pattern match.

Note: requires edition 2024 (edition = "2024" in Cargo.toml).

#028 Feb 2023

28. Raw string

Curious to know how you can add a double quote in raw strings?

No escape sequences are recognized in raw strings, so adding a backslash does not work.

Use ### to mark the start and the end of a raw string.

1
2
3
4
5
6
7
8
println!("This is a double quote \" ");

// println!(r"This is a double quote " "); // Nope!
// println!(r"This is a double quote \" "); // Nope!

// And this works
println!(r#"This is a double quote " "#);
println!(r###"This is a double quote " "###);
#027 Jan 2023

27. Option's sum/product

Rust’s option implements Sum and Product traits too!

Use it when you want to get None if there is a None element and sum of values otherwise.

1
2
3
let nums: [Option<u32>;3] = [Some(1), Some(10), None];
let maybe_nums: Option<u32> = nums.into_iter().sum();
assert_eq!(maybe_nums, None);

or sum of the values …

1
2
3
let nums = [Some(1), Some(10), Some(100)];
let maybe_nums: Option<u32> = nums.into_iter().sum();
assert_eq!(maybe_nums, Some(111));

And product.

1
2
3
let nums = [Some(1), Some(10), Some(100)];
let maybe_nums: Option<u32> = nums.into_iter().product();
assert_eq!(maybe_nums, Some(1000));
#026 Jan 2023

26. Collecting into Option

Rust’s option implements FromIterator too!

Use it when you want to get None if there is a None element and values otherwise.

1
2
3
let cars = [Some("Porsche"), Some("Ferrari"), None];
let maybe_cars: Option<Vec<_>> = cars.into_iter().collect();
assert_eq!(maybe_cars, None);

or values …

1
2
3
let cars = [Some("Porsche"), Some("Ferrari"), Some("Skoda")];
let maybe_cars: Option<Vec<_>> = cars.into_iter().collect();
assert_eq!(maybe_cars, Some(vec!["Porsche", "Ferrari", "Skoda"]));
Jan 2023

Implementing custom iterators

Iterators are a powerful feature in Rust that allow you to define how a collection of items should be processed. They are flexible, efficient, and easy to use. In this blog post, we will look at how to create custom iterators in Rust.

To create a custom iterator in Rust, you need to implement the Iterator trait for your type. This trait requires you to implement the next method, which returns the next item in the iteration. You can also implement the size_hint method to provide information about the size of the iterator, and the fold method to allow the iterator to be folded into a single value.

Here is a simple example of a custom iterator that counts up from a given number:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

struct CountUpFrom {
    start: u32,
}

impl Iterator for CountUpFrom {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.start < u32::MAX {
            self.start += 1;
            Some(self.start)
        } else {
            None
        }
    }
}

fn main() {
    let count_up_from = CountUpFrom { start: 10 };
    for i in count_up_from {
        println!("{}", i);
    }
}

This example creates a struct called CountUpFrom with a single field, start, which represents the number to start counting from. The CountUpFrom struct implements the Iterator trait, with u32 as the type of the items being iterated over. The next method increments the start field by one and returns it as an Option wrapped in a Some variant. If the start field has reached the maximum value of a u32, the next method returns None to indicate that the iteration is finished.

In the main function, we create an instance of CountUpFrom and use a for loop to iterate over it. This will print out the numbers 11 through u32::MAX to the console.

Custom iterators are a powerful and flexible tool in Rust, and can be used to create all sorts of interesting and useful iteration patterns. Give them a try in your next Rust project!

#025 Dec 2022

25. Option's iterator

Rust’s option implements an iterator!

1
2
3
4
let o: Option<u32> = Some(200u32);
let mut iter = o.into_iter();
let v = iter.next();
assert_eq!(v, Some(200u32));

Why is this useful? see example.

1
2
3
4
5
6
let include_this_pls = Some(300u32);
let r: Vec<u32> = (0..2).chain(include_this_pls).chain(2..5).collect();
assert_eq!(r, vec![0,1,300,2, 3,4]);
let but_not_this = None;
let r: Vec<u32> = (0..2).chain(but_not_this).chain(2..5).collect();
assert_eq!(r, vec![0,1,2,3,4]);
#024 Dec 2022

24. ..=X

As of 1.66, it is possible to use ..=X in patterns

1
2
3
4
5
6
let result = 20u32;

match result {
    0..=20 => println!("Included"),
    21.. => println!("Sorry"),
}
#023 Dec 2022

23. Enum's default value

Instead of manually implementing Default trait for an enum, you can derive it and explicitly tell which variant should be the default one.

1
2
3
4
5
6
7
8
9
#[derive(Default)]
enum Car{
    #[default]
    Porsche,
    Ferrari,
    Skoda
}

let car = Car::default();
#022 Nov 2022

22. Enum's Debug

Use Debug trait to print enum values if needed.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#[derive(Default,Debug)]
enum Car{
    #[default]
    Porsche,
    Ferrari,
    Skoda
}

let car = Car::default();
println!("{:?}", car);
#021 Oct 2022

21. Zip longest

Sometimes there is a need to zip two iterables of various lengths.

If it is known which one is longer, then use the following approach:

First one must be longer.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let nums1 = [1, 2, 3, 4];
let nums2 = [10, 20];

for (n1, n2) in nums1
    .into_iter()
    .zip(nums2.into_iter().chain(std::iter::repeat(0i32)))
{
    println!("{n1} - {n2}");
}
// Output:
// 1 - 10
// 2 - 20
// 3 - 0
// 4 - 0
#020 Sep 2022

20. let-else statements

As of 1.65, it is possible to use let statement with a refutable pattern.

1
2
3
4
5
6
7
let result: Result<i32, ()> = Ok(20);

let Ok(value) = result else {
  panic!("Heeeelp!!!");
};

assert_eq!(value, 20);
#019 Aug 2022

19. breaking from labeled blocks

As of 1.65, it is possible to label plain block expression and terminate that block early.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let result = 'block: {
    let result = 20i32;
    if result < 10 {
        break 'block 1;
    }
    if result > 10 {
        break 'block 2;
    }
    3
};
assert_eq!(result, 2);
#018 Jul 2022

18. flatten options

Use flatten to iterate over only Some values if you have a collection of Options.

1
2
3
4
5
6
let nums = vec![None, Some(2), None, Some(3), None];

let mut iter = nums.into_iter().flatten();

assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(3));
#016 Jun 2022

16. Option/Result match?!

Try to avoid matching Option or Result.

Use if let instead.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let result = Some(111);

// Not nice
match result {
    Some(x) => println!("{x}"),
    None => {}
};

// Better
if let Some(x) = result {
    println!("{x}");
}
#015 Apr 2022

15. Scan

Iterator adapter similar to fold (see previous bite) - holds some internal state and produces new iterator.

Note that Option is yielded from the closure.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let nums = [1, 2, 3];

let mut iter = nums.iter().scan(0, |acc, &x| {
    *acc = *acc + x;
    Some((*acc, x))
});

assert_eq!(iter.next(), Some((1, 1)));
assert_eq!(iter.next(), Some((3, 2)));
assert_eq!(iter.next(), Some((6, 3)));
#014 Apr 2022

14. Find index of item

Use position to find position of an element in iterator. Returns None if the element does not exist.

1
2
3
4
5
6
let nums = [1, 2, 3];

let pos = nums.iter().position(|value| *value == 3);
assert_eq!(pos, Some(2));
let pos = nums.iter().position(|value| *value == 10);
assert_eq!(pos, None);
#017 Mar 2022

17. filter_map

Similar to map but if allows to drop an item.

1
2
3
4
5
6
7
8
let text_nums = ["1", "two", "three", "4"];

let nums: Vec<_> = text_nums
    .iter()
    .filter_map(|x| x.parse::<u32>().ok())
    .collect();

assert_eq!(nums, vec![1, 4]);
#013 Mar 2022

13. Fold

Iterator consumer. Allows the accumulated value to be arbitrary type. Note the different types.

1
2
3
let nums: Vec<u8> = vec![1, 2, 3, 4];
let sum = nums.iter().fold(0u32, |acc, x| acc + *x as u32);
assert_eq!(sum, 10);
#012 Mar 2022

12. Enumerate

Use enumerate to convert iterator over (usize,item) pairs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let cars = vec!["Skoda", "Ferrari", "Ford"];

for (idx, car) in cars.iter().enumerate() {
    println!("{idx} - {car}");
}

// Output
// 0 - Skoda
// 1 - Ferrari
// 2 - Ford

Useful when index of item is needed.

#011 Mar 2022

11. Fuse

Fuse is an iterator that yields None forever after the underlying iterator yields None once. USage:

1
2
let values = [1,2,3,4];
let iter = values.iter().fuse();

Why is it useful? see example in this bite.

Sometimes an underlying iterator may or may yield Some(T) again after None was returned.

fuse ensures that after a None is returned for the first time, it always returns None.

Example from the rust documentation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct Alternate {
    state: i32,
}

impl Iterator for Alternate {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        let val = self.state;
        self.state = self.state + 1;

        if val % 2 == 0 {
            Some(val)
        } else {
            None
        }
    }
}

let mut iter = Alternate { state: 0 };

assert_eq!(iter.next(), Some(0));
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), None);

let mut iter = iter.fuse();

assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), None);

assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
#010 Feb 2022

10. cloned vs copied

Rust provides built-in methods to copy or clone elements when using an iterator.

Eg.

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).copied().collect();

What’s the difference? and when is it useful?

You may have done something like this:

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).map(|x| *x).collect();

but instead, use copied.

1
let nums: Vec<u32> = nums1.iter().chain(&nums2).copied().collect();

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
let nums = vec![1,2,3,4];
let nums2 = vec![5,6,7,8];

// doiing it manually by copying
// let all_nums: Vec<u32> = nums.iter().chain(&nums2).map(|x| *x).collect();

// doiing it manually by cloneing
// let all_nums: Vec<u32> = nums.iter().chain(&nums2).map(|x| x.clone()).collect();

// but why not just use provided functionality ??!
let all_nums: Vec<u32> = nums.iter().chain(&nums2).copied().collect();

// or cloned if copy it is not an option
//let all_nums: Vec<u32> = nums.iter().chain(&nums2).cloned().collect();

 assert_eq!(all_nums, vec![1,2,3,4,5,6,7,8]);

Difference between cloned and copied?

Same reasoning aplies as for Copy and Clone traits. Use copied to avoid to accidentally cloing iterator elements. Use copied when possible.

#009 Feb 2022

9. Inspecting iterator

Ever wondered how to print while iterating?

Use inspect.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let cars = vec!["Skoda", "Ferrari", "Ford"];

let car_lenghts: Vec<u32> = cars
    .iter()
    .enumerate()
    .inspect(|(idx, s)| println!("{idx} - {s}"))
    .map(|(_, name)| name.len() as u32)
    .collect();

assert!( car_lenghts == vec![5,7,4
#008 Feb 2022

8. Drain

Use drain to remove specified range from a vector.

1
2
3
4
5
let mut a = vec![1,2,3,4,5];

let _ = a.drain(0..3);

assert!(a == vec![4,5]);

What is the difference to call into_iter which returns T ?

into_iter takes the collection by value and consumes it.

drain borrows mutable reference, and returns a drain iterator of elements. If such iterator is dropped without exhausting it, all elements are dropped.

#007 Feb 2022

7. iter() vs into_iter()

What is the difference between .iter() and .into_iter()?

iter yields &T

into_iter may yield any of T, &T or &mut T based on context.

1
2
3
4
5
6
7
8
9
let cars = vec!["Skoda", "Ferrari", "Ford"];

for car in cars.iter() {
    println!("{car}");
}

for car in cars.into_iter() {
    println!("{car}");
}

this works but …

This does not. This results in compile error because cars are moved due to into_iter call.

1
2
3
4
5
6
7
8
9
let cars = vec!["Skoda", "Ferrari", "Ford"];

for car in cars.into_iter() {
  println!("{car}");
}

for car in cars.iter() {
    println!("{car}");
}
#006 Jan 2022

6. non-exhaustive enums

Use non-exhaustive attribute to indicate that enum may have more variants in future.

1
2
3
4
5
#[non_exhaustive]
enum Event{
  Error,
  Completed(String)
}

If such enum is used in match pattern, it is required to handle ’_’ pattern.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let event = Event::Error;
match event {
    Event::Error => {
        println!("error")
    }
    Event::Completed(s) => {
        println!("completed {}", s)
    }
    _ => {
        println!("unknown")
    }
}

How is this useful?

If you are a maintainer of a library and you’d expect to add more variants in the future.

This approach helps you to build a library with non-breaking code changes for library users.

#005 Jan 2022

5. Loop breaks and labels

Breaks and labels in loops?

IT is possible to use a label to specify which enclosing loop is affected.

1
2
3
4
5
6
7
8
let s = 'outer: loop {
      for idx in 0..10 {
          if idx == 4 {
              break 'outer idx;
          }
      }
  };
assert!(s == 4);
#004 Jan 2022

4. Option -> Result

Convert Option to Result easily.

1
2
3
let o: Option<u32> = Some(200u32);
let r: Result<u32,()> = o.ok_or(());
assert_eq!(r, Ok(200u32));
#003 Jan 2022

3. Result -> Option

Convert Result to Option easily.

1
2
3
let r: Result<u32,()> = Ok(200u32);
let o: Option<u32> = r.ok();
assert_eq!(o, Some(200u32));
#002 Jan 2022

2. mysterious @

Use @ to bind a pattern to a name.

1
2
3
4
5
6
let foo = 4;

match foo {
    x @ 0..=5 => assert_eq!(x,4),
    y @ _ => panic!("{}-too many!", y)
}
1
2
3
4
5
6
let point: (u32,u32) = (1, 2);

match point {
    (1, y @ _) => assert_eq!(y,2),
    _ => {}
}
1
2
3
4
5
let point: (u32,u32) = (1, 2);
let mypoint @ (x, _) = point;

assert_eq!(x,1);
assert_eq(mypoint,point);
#001 Jan 2022

1. matches!

Need to check if an expression matches certain pattern?

1
2
let foo = vec!['1','2'];
assert!(matches!(foo.len(), 0..=1));

The example above checks if len is 0 or 1.

Or

1
2
3
let bar = Some(4);

assert!(matches!(bar, Some(x) if x > 2));
#000 Jan 2022

0. Hello, rustbites!

Hello and welcome to rustbites.com

1
2
let hello = "Hello, rustbites!";
println!("{}", hello);

explanation and details