ChristopherBiscardi / advent-of-code

234 stars 47 forks source link

Question about nom #2

Closed florentsorel closed 1 year ago

florentsorel commented 1 year ago

Hi!

Sorry to ask a question here but I'm become nuts. Disclaimer: I'm a beginner with Rust.

In your example here you unwrap (it's the solution which work for me) and I need to do the same but I want to use ? operator instead but the compiler tell me bad things and I don't known how to fix that. So I found your github and... you unwrap too so I don't know how to handle the error because I want to do a library so I need to handle error.

My goal is to parse a string slice like 04:53:33,324 or 2:3:44,123 which represent hour:minute:second,millisecond (my end goal is to parse a srt file).

If you have some time for that, I will really appreciate your help and some explaination because nom is awesome but it's hard too.

#[derive(Debug)]
struct Time {
    hour: u8,
    // minute: u8,
    // second: u8,
    // millisecond: u16,
}

impl Time {
    fn parse(input: &str) -> IResult<&str, Self> {
        let (input, hour) = digit1(input)?;
        println!("{:#?}", hour);
        Ok((
            input,
            Time {
                hour: hour.parse::<u8>().unwrap(),
            },
        ))
    }
}

I want to handle the error if the input is alphabetic for example.

Thx.

utaqhy commented 1 year ago

Well, I stumbled across this question a while ago.

Using unwrap is considered to be a bit sloppy when it comes to production ready code. In some cases it might be fine, if you are sure that the code does not panic.

Parsing a string into an integer with parse returns an enum Result. This Result can be Ok or Err. A very detailed and excellent explanation can be found in the rust book: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html

For example:

let result_hour: Result<u8, ParseIntError> = hour.parse::<u8>();
let maybe_hour = match result_hour {
    Ok(int_hour) => int_hour,
    Err(_) => {
        // do something in this scope
    },
};

In the first line, result_hour represents an enum Result. The type annotation Result<u8, ParseIntError> is just for explanation and is not needed in the code. With match we can get the value (of type u8) if result_hour is Ok (which basically means that parsing the string to an integer worked) or do something else if parsing resulted into an error (Err). Note that the scope opened after Err(_) => should return an integer of type u8 or panic.

ChristopherBiscardi commented 1 year ago

If you want to use nom, @rtransat , this is a program that will do what you ask https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bf0b8bc8faa1ece1c1130f152e8039d1

The original code of mine that you link to was written two years ago, so I'm not sure if the number parsers existed in that version of nom, but they definitely do now so definitely use them. Specifically the u32 parser here is used in the tuple sequence.

use nom::{
    bytes::complete::tag,
    character::complete::u32,
    sequence::{preceded, tuple},
    *,
}; // 7.1.1

#[derive(Debug)]
struct SrtTime {
    hour: u32,
    minute: u32,
    second: u32,
    millisecond: u32,
}

fn srt_time(input: &str) -> IResult<&str, SrtTime> {
    let (input, values) = tuple((
        u32,
        preceded(tag(":"), u32),
        preceded(tag(":"), u32),
        preceded(tag(","), u32),
    ))(input)?;
    dbg!(values);
    Ok((
        input,
        SrtTime {
            hour: values.0,
            minute: values.1,
            second: values.2,
            millisecond: values.3,
        },
    ))
}

fn main() {
    let first = "04:53:33,324";
    let second = "2:3:44,123";
    let (_input, time_1) = srt_time(first).unwrap();
    let (_input, time_2) = srt_time(second).unwrap();
    dbg!(time_1, time_2);
}