rust 58 lines · 8 steps

Parsing duration strings safely in Rust

A single-pass parser turns strings like "1h30m" into a Duration while catching every malformed or overflowing input.

Explained by highlit
1use std::time::Duration;
2 
3#[derive(Debug, PartialEq)]
4pub enum ParseDurationError {
5 Empty,
6 UnexpectedChar(char),
7 MissingUnit,
8 Overflow,
9}
10 
11pub fn parse_duration(input: &str) -> Result<Duration, ParseDurationError> {
12 let input = input.trim();
13 if input.is_empty() {
14 return Err(ParseDurationError::Empty);
15 }
16 
17 let mut total_secs: u64 = 0;
18 let mut current: u64 = 0;
19 let mut has_digits = false;
20 
21 for ch in input.chars() {
22 match ch {
23 '0'..='9' => {
24 current = current
25 .checked_mul(10)
26 .and_then(|v| v.checked_add((ch as u8 - b'0') as u64))
27 .ok_or(ParseDurationError::Overflow)?;
28 has_digits = true;
29 }
30 _ => {
31 if !has_digits {
32 return Err(ParseDurationError::UnexpectedChar(ch));
33 }
34 let multiplier = match ch {
35 'd' => 86_400,
36 'h' => 3_600,
37 'm' => 60,
38 's' => 1,
39 other => return Err(ParseDurationError::UnexpectedChar(other)),
40 };
41 let segment = current
42 .checked_mul(multiplier)
43 .ok_or(ParseDurationError::Overflow)?;
44 total_secs = total_secs
45 .checked_add(segment)
46 .ok_or(ParseDurationError::Overflow)?;
47 current = 0;
48 has_digits = false;
49 }
50 }
51 }
52 
53 if has_digits {
54 return Err(ParseDurationError::MissingUnit);
55 }
56 
57 Ok(Duration::from_secs(total_secs))
58}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Modeling each failure mode as an enum variant makes error handling exhaustive and self-documenting.
  2. 2Checked arithmetic converts silent overflow into an explicit, recoverable error.
  3. 3A couple of accumulator variables let you parse in one pass without allocating substrings.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Parsing duration strings safely in Rust — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code