#217 Jun 22, 2026

217. eq_ignore_ascii_case — Case-Insensitive Compare Without the Allocation

a.to_lowercase() == b.to_lowercase() allocates two fresh Strings just to throw them away after the comparison. For ASCII text — headers, extensions, keywords — eq_ignore_ascii_case does it byte-by-byte with zero allocations.

The reflexive way to compare strings case-insensitively is to lowercase both sides and check for equality:

1
2
3
4
5
let header = "Content-Type";
let wanted = "content-type";

// Two heap allocations, then immediately dropped
assert!(header.to_lowercase() == wanted.to_lowercase());

That’s two Strings built on the heap for a question that’s really just “are these equal if we ignore case?” The standard library answers it directly, walking both byte sequences and folding AZ against az as it goes — no allocation, and it bails early on the first mismatch:

1
2
3
let header = "Content-Type";

assert!(header.eq_ignore_ascii_case("content-type"));

It’s defined on [u8] too, which is handy when you’re matching protocol tokens straight out of a buffer without first validating UTF-8:

1
assert!(b"GET".eq_ignore_ascii_case(b"get"));

The one thing to know: it folds only ASCII letters. Non-ASCII bytes must match exactly, so it won’t treat Ä and ä as equal, and it won’t expand ß:

1
2
assert!(!"Ä".eq_ignore_ascii_case("ä"));
assert!(!"Straße".eq_ignore_ascii_case("STRASSE"));

For human-facing, multilingual text you still want full Unicode case folding. But for the things you actually compare case-insensitively in systems code — HTTP methods and header names, file extensions, config keys, command names — the input is ASCII by definition, and reaching for eq_ignore_ascii_case is both faster and clearer:

1
2
3
4
5
6
7
fn is_jpeg(ext: &str) -> bool {
    ext.eq_ignore_ascii_case("jpg") || ext.eq_ignore_ascii_case("jpeg")
}

assert!(is_jpeg("JPG"));
assert!(is_jpeg("Jpeg"));
assert!(!is_jpeg("png"));

If you need to fold case in place rather than compare, make_ascii_lowercase mutates a &mut str or &mut [u8] without allocating either — same ASCII-only rule applies.

← Previous 216. unsigned_abs — i32::MIN Has No Positive Twin, So .abs() Overflows Next → 218. str::match_indices — Find Every Match and Its Position in One Pass