#221 Jun 25, 2026

221. from_str_radix — Parse Hex, Binary, or Octal Without Hand-Rolling a Loop

Got a "ff" or a "1010" and need the number behind it? Don’t loop over the characters multiplying by 16. Every integer type has from_str_radix, which parses a string in any base from 2 to 36 in one call.

The hand-rolled version is easy to get subtly wrong — overflow, bad digits, off-by-one on the place value:

1
2
3
4
5
6
7
8
fn parse_hex(s: &str) -> u32 {
    let mut n = 0u32;
    for c in s.chars() {
        n = n * 16 + c.to_digit(16).unwrap();
    }
    n
}
let _ = parse_hex("ff"); // works, but silently overflows on long input

from_str_radix does the whole thing, and returns a Result so bad input is an error instead of a panic or a wrong answer:

1
2
3
4
5
6
7
8
let n = u32::from_str_radix("ff", 16).unwrap();
assert_eq!(n, 255);

let b = u8::from_str_radix("1010", 2).unwrap();
assert_eq!(b, 10);

let o = u16::from_str_radix("755", 8).unwrap();
assert_eq!(o, 493);

It validates digits for you — a character outside the chosen base is a clean Err, not garbage:

1
2
assert!(u32::from_str_radix("xyz", 16).is_err());
assert!(u8::from_str_radix("2", 2).is_err()); // '2' isn't a binary digit

A real use: cracking a #RRGGBB color into channels. Slice, parse, done — no manual nibble math:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn rgb(hex: &str) -> Option<(u8, u8, u8)> {
    let h = hex.strip_prefix('#').unwrap_or(hex);
    if h.len() != 6 { return None; }
    let r = u8::from_str_radix(&h[0..2], 16).ok()?;
    let g = u8::from_str_radix(&h[2..4], 16).ok()?;
    let b = u8::from_str_radix(&h[4..6], 16).ok()?;
    Some((r, g, b))
}

assert_eq!(rgb("#E8593C"), Some((232, 89, 60)));
assert_eq!(rgb("oops"), None);

Signed types work too, and a leading - is honored:

1
assert_eq!(i32::from_str_radix("-2a", 16), Ok(-42));

For plain base-10 you don’t even need it — "42".parse::<u32>() is the same thing with the radix fixed at 10. Reach for from_str_radix the moment the base isn’t ten.

← Previous 220. ilog10 — Count an Integer's Digits Without Formatting It to a String Next → 222. HashSet::intersection / union / difference — Set Math Without the Manual Loops