Search code examples
rustiterator

How to convert 2 bounded loop to iteration syntax


How can I convert this loop based implementation to iteration syntax?

fn parse_number<B: AsRef<str>>(input: B) -> Option<u32> {
    let mut started = false;
    let mut b = String::with_capacity(50);
    let radix = 16;

    for c in input.as_ref().chars() {
        match (started, c.is_digit(radix)) {
            (false, false) => {},
            (false, true) => {
                started = true;
                b.push(c);
            },
            (true, false) => {
                break;
            }
            (true, true) => {
                b.push(c);
            },
        }
    }

    if b.len() == 0 {
        None
    } else {
        match u32::from_str_radix(b.as_str(), radix) {
            Ok(v) => Some(v),
            Err(_) => None,
        }
    }
}

The main problem that I found is that you need to terminate the iterator early and be able to ignore characters until the first numeric char is found.

  • .map_while() fails because it has no state.
  • .reduce() and .fold() would iterate over the entire str regardless if the number has already ended.

Solution

  • It looks like you want to find the first sequence of digits while ignoring any non-digits before that. You can use a combination of .skip_while and .take_while:

    fn parse_number<B: AsRef<str>>(input: B) -> Option<u32> {
        let input = input.as_ref();
        let radix = 10;
    
        let digits: String = input.chars()
            .skip_while(|c| !c.is_digit(radix))
            .take_while(|c| c.is_digit(radix))
            .collect();
        
        u32::from_str_radix(&digits, radix).ok()
    }
    
    fn main() {
        dbg!(parse_number("I have 52 apples"));
    }
    
    [src/main.rs:14] parse_number("I have 52 apples") = Some(
        52,
    )