Syntax confusion and nom

Quite new to rust and started to parsing files.
Now I am a little confused of the syntax.
Why does the following code work when I call parse_linestr, but does not work when I call the contained code directly ? I get the error:

the trait bound `&std::string::String: nom::Slice<std::ops::RangeFrom<usize>>` is not satisfied

the trait `nom::Slice<std::ops::RangeFrom<usize>>` is not implemented for `&std::string::String`

The code in question:

fn parse_linestr(linestr: &str) -> nom::IResult<&str, char> {
    nom::character::complete::char('*')(linestr)
}

fn main() -> io::Result<()>{
    let f = File::open("somefilename")?;
    let f = BufReader::new(f);

    for line in f.lines() {
        let linestr = &line.unwrap();
        let pr = parse_linestr(linestr); //works
        let pr = nom::character::complete::char('*')(linestr); //error
    }
    Ok(())
}

More general, what is this syntax with double ()() anyway ?

From the nom source:

pub fn char<I, Error: ParseError<I>>(c: char) -> impl Fn(I) -> IResult<I, char, Error>
where
  I: Slice<RangeFrom<usize>> + InputIter,
  <I as InputIter>::Item: AsChar,
{
  move |i: I| match (i).iter_elements().next().map(|t| {
    let b = t.as_char() == c;
    (&c, b)
  }) {
    Some((c, true)) => Ok((i.slice(c.len()..), c.as_char())),
    _ => Err(Err::Error(Error::from_char(i, c))),
  }
}

linestr is a &String. When you call pars_linestr, you coerce it to a &str and pass that to the parser function. Apparently, parsing is only supported for &str and not &String but the conversion is trivial: you could either just call as_str() on the &String, or apply a deref coercion, which is exactly what happens when you pass a &String to a function expecting a &str.

As to the double ()(), there's nothing special about that. It's just a repeated function call. A parser is a function, so a function that creates a parser returns another function. You can see this by looking at its return type, impl Fn(…) -> …. So, after you get hold of this parser function, you have to call it in order to actually perform the parsing.

See, Rust expressions (and many other aspects and features of the language, e.g. types) are composable. Just because we don't usually use some combinations doesn't mean they are impossible or forbidden. For example, you could apply !!! to a Boolean or integer expression and it would have the same effect as applying ! only once. We don't do it because it's needlessly complicated, but it's nevertheless allowed. Likewise, functions returning functions is a rare pattern, but there's still nothing special about it, as functions are just as first-class in the language as other, non-function types.

1 Like

Thank you, the as_str() was the missing piece.
Probably I need to get accustomed to the diffrent string types. :grinning:

And the function as return type, didn't think about that, makes sense.

let pr :IResult<&str,char> = nom::character::complete::char('*')(linestr.as_str());

Do you know why the compiler can not infer the corrent type, and I need to specifiy the IResult ?