#216 Jun 2026

216. unsigned_abs — i32::MIN Has No Positive Twin, So .abs() Overflows

(-5i32).abs() is fine, but i32::MIN.abs() panics — the magnitude 2147483648 doesn’t fit back into an i32. unsigned_abs returns the magnitude in the unsigned type, where it always fits.

Two’s complement isn’t symmetric. An i32 reaches down to -2147483648 but only up to 2147483647, so the most-negative value has no positive counterpart. That makes abs a landmine on exactly one input:

1
2
3
4
5
let x: i32 = -5;
assert_eq!(x.abs(), 5); // fine

let edge = i32::MIN;
let m = edge.abs(); // panics in debug, wraps to i32::MIN in release

The same trap hides inside the classic (a - b).abs() distance trick, which is why abs_diff exists for differences. But when you have a single signed value and just want its magnitude, the fix is unsigned_abs:

1
2
3
let edge = i32::MIN;

assert_eq!(edge.unsigned_abs(), 2147483648u32);

It returns the unsigned sibling type (i32u32, i8u8, …), which is wide enough to hold the magnitude of every input — including the awkward one — so it can never overflow:

1
2
3
assert_eq!((-5i32).unsigned_abs(), 5u32);
assert_eq!((127i8).unsigned_abs(), 127u8);
assert_eq!(i8::MIN.unsigned_abs(), 128u8); // .abs() would panic here

If you genuinely need a signed result and want to handle the edge instead of ignoring it, checked_abs hands you an Option and saturating_abs clamps to MAX:

1
2
assert_eq!(i32::MIN.checked_abs(), None);
assert_eq!(i32::MIN.saturating_abs(), i32::MAX);

Any time you take the absolute value of a signed integer that could conceivably be MIN — parsed input, a subtraction result, a delta from untrusted data — reach for unsigned_abs instead of abs and the panic simply can’t happen.

#215 Jun 2026

215. next_multiple_of — Round Up to a Multiple Without the +m-1 Dance

Padding a length up to the next multiple of 8? The classic (n + 7) / 8 * 8 works right up until it overflows or you fat-finger the constant. next_multiple_of says exactly what you mean.

The hand-rolled version shows up everywhere alignment matters — buffer sizes, page rounding, table padding:

1
2
3
4
// Easy to get subtly wrong, and `n + m - 1` can overflow near the top of the range
fn round_up(n: usize, m: usize) -> usize {
    (n + m - 1) / m * m
}

Every integer type has next_multiple_of: the smallest value >= self that is a multiple of the argument. Already a multiple? It’s returned unchanged.

1
2
3
4
assert_eq!(13u32.next_multiple_of(8), 16); // round up
assert_eq!(16u32.next_multiple_of(8), 16); // already aligned, untouched
assert_eq!(0u32.next_multiple_of(8), 0);
assert_eq!(23u32.next_multiple_of(10), 30);

When the rounded value would overflow, next_multiple_of panics — but checked_next_multiple_of hands you an Option instead, so you stay in control:

1
2
assert_eq!(250u8.checked_next_multiple_of(8), None); // 256 won't fit in u8
assert_eq!(13u8.checked_next_multiple_of(8), Some(16));

One method, the intent on the page, and no off-by-one waiting to bite you.

#214 Jun 2026

214. slice::windows — Compare Consecutive Elements Without the Index Math

The classic “look at each element and the next one” loop is a panic waiting to happen: 0..v.len() - 1 underflows the moment the slice is empty. windows hands you every consecutive pair safely, no arithmetic required.

Whenever you need to look at neighbouring elements — deltas between samples, “is each one bigger than the last”, spotting a transition — the reflex is to index by hand:

1
2
3
4
5
6
let v = [3, 7, 2, 9];

for i in 0..v.len() - 1 {
    let diff = v[i + 1] - v[i];
    // ...
}

This works until v is empty. v.len() is 0, v.len() - 1 underflows on usize, and the loop panics before it even starts. You end up bolting on an if !v.is_empty() guard, and the v[i + 1] indexing still pays a bounds check every iteration.

slice::windows(n) gives you an iterator of overlapping sub-slices of length n. Ask for 2 and you get each consecutive pair, with no index juggling and no empty-slice trap:

1
2
3
4
5
6
7
let v = [3, 7, 2, 9];

let diffs: Vec<i32> = v.windows(2)
    .map(|w| w[1] - w[0])
    .collect();

assert_eq!(diffs, vec![4, -5, 7]);

Because windows yields nothing when the slice is shorter than n, the empty and single-element cases just fall out for free — no special handling:

1
2
3
4
5
let empty: [i32; 0] = [];
assert_eq!(empty.windows(2).count(), 0);

let one = [42];
assert_eq!(one.windows(2).count(), 0);

It composes with the rest of the iterator toolbox, too. Checking that a slice is strictly increasing becomes one readable line:

1
2
3
let v = [1, 4, 6, 10];
let strictly_increasing = v.windows(2).all(|w| w[0] < w[1]);
assert!(strictly_increasing);

The window size isn’t limited to 2 — pass 3 for sliding triples (handy for smoothing or peak detection), and each window is a real slice you can index, sum, or pattern-match:

1
2
3
let v = [1, 2, 3, 4, 5];
let sums: Vec<i32> = v.windows(3).map(|w| w.iter().sum()).collect();
assert_eq!(sums, vec![6, 9, 12]);

One caveat: the windows overlap and share elements, so windows only hands out shared (&) references — you can’t mutate through them. When you want non-overlapping fixed-size groups instead, reach for chunks. But for anything that compares an element to its neighbours, windows replaces the fragile len() - 1 loop with code that can’t go out of bounds.

#213 Jun 2026

213. abs_diff — The Gap Between Two Numbers, No Underflow, No Overflow

Subtracting two unsigned integers to get their distance is a panic waiting to happen — and the signed (a - b).abs() workaround overflows just as quietly. abs_diff gives you the gap correctly, every time.

The reflex for “how far apart are these two numbers” is a plain subtraction. On unsigned types that underflows the moment the second value is larger:

1
2
3
4
let a: u32 = 10;
let b: u32 = 25;

let gap = a - b; // panics in debug, wraps to 4294967281 in release

So you reach for the defensive guard, branching on which side is bigger:

1
let gap = if a > b { a - b } else { b - a };

The signed version looks safer but isn’t. (a - b).abs() overflows when the two values are far apart, and i32::MIN.abs() panics outright because 2147483648 doesn’t fit in an i32.

Since Rust 1.60, every integer type has abs_diff — order-independent, branch-free, and immune to both traps:

1
2
3
4
5
let a: u32 = 10;
let b: u32 = 25;

assert_eq!(a.abs_diff(b), 15);
assert_eq!(b.abs_diff(a), 15);

On signed integers it returns the unsigned counterpart, which is what makes it total. The distance between the extremes is genuinely larger than i32::MAX, and abs_diff represents it without flinching:

1
2
assert_eq!((-5i32).abs_diff(5), 10u32);
assert_eq!(i32::MIN.abs_diff(i32::MAX), u32::MAX);

That last gap — 4294967295 — can’t be held in an i32 at all, which is exactly why (min - max).abs() was never going to work.

Any time you’re computing a difference and only care about magnitude — pixel distances, clock skew, tolerance checks, diffing counters — reach for abs_diff instead of subtracting and hoping the bigger number is on the left.

#212 Jun 2026

212. slice::fill / fill_with — Reset Every Element Without the Loop

Clearing a buffer with a hand-rolled for loop is noise. slice::fill overwrites every element in one call — and fill_with builds a fresh value per slot.

You’ve got a buffer to reset between iterations, so the reflex is to write the loop:

1
2
3
for x in buf.iter_mut() {
    *x = 0;
}

fill says the same thing in one line, and it pairs perfectly with the reuse-a-buffer trick: keep the allocation, wipe the contents.

1
2
3
let mut buf = vec![7, 7, 7, 7];
buf.fill(0);
assert_eq!(buf, [0, 0, 0, 0]);

It works on any &mut [T] where T: Clone, so array and slice subranges are in too:

1
2
3
let mut grid = [[1u8; 3]; 2];
grid[0].fill(9);
assert_eq!(grid[0], [9, 9, 9]);

The catch: fill clones one value into every slot. When you need a distinct value per element — a counter, a random number, a fresh allocation — reach for fill_with, which calls the closure once per slot:

1
2
3
4
5
6
7
let mut ids = [0u32; 4];
let mut next = 0;
ids.fill_with(|| {
    next += 1;
    next
});
assert_eq!(ids, [1, 2, 3, 4]);

Use fill(value) when every slot is the same, and fill_with(|| ...) when each slot earns its own. Either way: no index, no loop, no off-by-one.

#211 Jun 2026

211. Iterator::inspect — Debug a Lazy Chain Without Tearing It Apart

When a map/filter chain gives the wrong answer, the reflex is to rip it into a for loop just to drop in a println!. You don’t have to — inspect lets you peek at every value as it flows past.

inspect runs a closure on each item and then hands the same item downstream, untouched. It’s a tap on the pipe: perfect for seeing what survives each stage without restructuring anything.

Say this sum is wrong and you want to know which numbers actually make it through the filter:

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

let sum: i32 = nums
    .iter()
    .filter(|&&n| n % 2 == 0)
    .inspect(|n| println!("kept: {n}"))
    .map(|&n| n * 10)
    .inspect(|n| println!("scaled: {n}"))
    .sum();

assert_eq!(sum, 120); // (2 + 4 + 6) * 10

You can drop a tap between any two adapters, as many as you like, and the result is identical to the chain without them. Because iterators are lazy, the closures fire only as items are pulled — so the prints interleave with the real work instead of dumping everything up front.

The key property is that inspect is transparent: it borrows each item (&T) and returns the iterator unchanged, so types and values flow through exactly as before:

1
2
3
4
5
6
let evens: Vec<i32> = (1..=6)
    .inspect(|n| eprintln!("saw: {n}"))
    .filter(|n| n % 2 == 0)
    .collect();

assert_eq!(evens, [2, 4, 6]);

Reach for eprintln! inside the closure so debug output goes to stderr and stays out of any data you’re printing to stdout. And when you’re done, deleting the tap is a one-line change — not an unwind of the loop you’d otherwise have written.

210. next_power_of_two — Round Up to a Power of Two Without the Bit-Twiddling

Sizing a buffer or hash table to the next power of two is a classic — and people keep reinventing it with shifts and leading_zeros. The standard library already has it.

You’ve probably seen (or written) the bit-twiddling version, which is easy to get subtly wrong on edge cases like 0 or exact powers:

1
2
3
4
5
6
7
8
9
fn round_up_manual(n: u32) -> u32 {
    let mut v = n - 1;        // underflows when n == 0
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v + 1
}

next_power_of_two does exactly this, correctly, for every unsigned integer type:

1
2
3
4
assert_eq!(5u32.next_power_of_two(), 8);
assert_eq!(8u32.next_power_of_two(), 8);   // already a power of two
assert_eq!(1u32.next_power_of_two(), 1);
assert_eq!(0u32.next_power_of_two(), 1);   // the tricky one, handled

A common use is rounding an allocation up so masking can replace modulo:

1
2
3
4
5
6
7
let requested = 100usize;
let cap = requested.next_power_of_two();   // 128
assert_eq!(cap, 128);

// because cap is a power of two, idx % cap == idx & (cap - 1)
let idx = 1234usize;
assert_eq!(idx % cap, idx & (cap - 1));

The one trap: if the next power of two doesn’t fit in the type, next_power_of_two panics in debug and wraps to 0 in release. When the input is untrusted, reach for checked_next_power_of_two, which hands you an Option instead:

1
2
3
assert_eq!(200u8.next_power_of_two(), 0);          // wraps in release — bug waiting to happen
assert_eq!(200u8.checked_next_power_of_two(), None); // explicit, safe
assert_eq!(100u8.checked_next_power_of_two(), Some(128));
#209 Jun 2026

209. ..Default::default() — set the fields you care about, default the rest

Building a struct with a dozen fields when you only want to change one is tedious. Struct update syntax lets you fill in the rest from a default — or from any other value.

Say you have a config struct. Spelling out every field just to flip one flag is noise:

1
2
3
4
5
6
7
let cfg = ServerConfig {
    host: String::new(),
    port: 8080,
    max_connections: 0,
    use_tls: true,
    timeout_secs: 0,
};

Derive Default, then use ..Default::default() to fill the fields you didn’t mention:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#[derive(Debug, Default, PartialEq)]
struct ServerConfig {
    host: String,
    port: u16,
    max_connections: u32,
    use_tls: bool,
    timeout_secs: u64,
}

let cfg = ServerConfig {
    port: 8080,
    use_tls: true,
    ..Default::default()
};

assert_eq!(cfg.port, 8080);
assert_eq!(cfg.use_tls, true);
assert_eq!(cfg.host, "");        // String::default()
assert_eq!(cfg.max_connections, 0);

The ..base part isn’t limited to Default — you can spread from any existing instance, so it doubles as a cheap “clone but tweak”:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let base = ServerConfig {
    max_connections: 1024,
    ..Default::default()
};

let derived = ServerConfig {
    port: 9090,
    ..base
};

assert_eq!(derived.port, 9090);
assert_eq!(derived.max_connections, 1024);

Two things to remember: the ..rest has to come last, and the fields it pulls in are moved out of the source value (or copied, if they’re Copy) — so a non-Copy field like host means you can’t keep using base afterward.

208. f64::mul_add — One Rounding, One Instruction, Better Accuracy

a * b + c rounds twice and may compile to two separate operations. a.mul_add(b, c) computes the whole thing at full precision, rounds once, and on a CPU with FMA folds into a single instruction.

Two roundings vs. one

Floating-point math rounds after every operation. Writing a * b + c first rounds the product a * b to the nearest f64, then rounds again after adding c. That intermediate rounding throws away bits before they ever reach the sum:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let a = 1.0_f64;
let b = 2.0_f64;
let c = 3.0_f64;

// Two roundings: (a*b) rounded, then (+ c) rounded
let separate = a * b + c;

// One rounding: a*b + c evaluated at full precision, rounded once
let fused = a.mul_add(b, c);

assert_eq!(separate, 5.0);
assert_eq!(fused, 5.0);

For tidy values they agree. The difference shows up when the product needs more bits than an f64 can hold and the addition would have recovered them — exactly the catastrophic-cancellation cases that wreck numeric code.

Where it pays off: Horner’s method

Polynomial evaluation is built out of multiply-then-add, so it’s the textbook home for mul_add. To evaluate 2x² + 3x + 4 with Horner’s method you nest the operations, and each step is one fused step:

1
2
3
4
5
6
fn poly(x: f64) -> f64 {
    // ((2*x) + 3)*x + 4
    2.0_f64.mul_add(x, 3.0).mul_add(x, 4.0)
}

assert_eq!(poly(2.0), 18.0); // 2*4 + 3*2 + 4

Each mul_add keeps full precision through the chain instead of rounding at every * and +. The same pattern carries dot products, which are a sum of products:

1
2
3
4
5
6
7
fn dot(a: &[f64], b: &[f64]) -> f64 {
    a.iter()
        .zip(b)
        .fold(0.0, |acc, (&x, &y)| x.mul_add(y, acc))
}

assert_eq!(dot(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]), 32.0);

The honest caveat

mul_add is faster only when the target CPU has a hardware fused-multiply-add instruction (modern x86-64 with FMA, AArch64, most others you’ll deploy to). Where it doesn’t, the standard library has to emulate the exact single-rounding semantics in software, and that emulation is slower than a plain a * b + c. So this is a hot-path tool: reach for it in tight numeric loops on hardware you control (or behind target-feature/target-cpu flags), and lean on it freely when you need the extra accuracy regardless of speed.

The bottom line

mul_add gives you a single rounding step and, on capable hardware, a single instruction for a * b + c. Use it in polynomial evaluation, dot products, and any multiply-accumulate loop where precision or throughput matters — and remember it can regress on FMA-less targets, so measure when speed is the goal.

207. if let Guards — Match a Pattern Inside a Guard, and Still Fall Through

A match arm whose extra check is itself a fallible if let used to force you to nest a second match — and duplicate the fallback. Rust 1.95 lets the guard do the binding.

Say a config field is a raw string and you want to parse it as a port, defaulting when it’s absent or garbage:

1
2
3
4
enum Field {
    Raw(String),
    Missing,
}

Before if let guards, a guard could only hold a bool expression — it couldn’t bind. So the parse had to move inside the arm body, and the fallback ended up written twice:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fn to_port(f: &Field) -> u16 {
    match f {
        Field::Raw(s) => {
            if let Ok(p) = s.parse::<u16>() {
                p
            } else {
                8080 // fallback #1: Raw but unparseable
            }
        }
        Field::Missing => 8080, // fallback #2: same value, second copy
    }
}

Two copies of 8080 that have to stay in sync. The nested if let can’t fall through to another arm — once you’re in Field::Raw, you’re stuck handling everything there.

Since Rust 1.95, if let works directly in the guard. If the pattern fails to match, the arm is skipped and matching continues to the next arm:

1
2
3
4
5
6
fn to_port(f: &Field) -> u16 {
    match f {
        Field::Raw(s) if let Ok(p) = s.parse::<u16>() => p,
        _ => 8080, // one fallback, covers Missing AND unparseable Raw
    }
}

The guard both tests and binds p, and a failed parse simply drops to the catch-all. One fallback, no duplication.

It composes with && too, so you can chain several binds in one guard:

1
2
3
4
5
6
7
8
9
fn first_two(line: &str) -> Option<(i32, i32)> {
    let mut it = line.split(',');
    match (it.next(), it.next()) {
        (Some(a), Some(b))
            if let Ok(x) = a.trim().parse::<i32>()
            && let Ok(y) = b.trim().parse::<i32>() => Some((x, y)),
        _ => None,
    }
}

Reach for it whenever a match arm needs a second, fallible look at its data and you want a clean failure to roll into the next arm instead of a pyramid of nested matches.