Skip to content

nom

Serialization & FormatsXML / ParsingRust

What it is

nom is a parser combinator library for Rust that builds parsers from small composable functions, working on bytes or strings with zero copying.

Each parser takes input and returns the remaining input plus a value. Combinators sequence, alternate and repeat them into a complete grammar.

Installation

cargo add nom

Getting started

The smallest useful thing you can do with it, and what each part means.

Composing small parsers
use nom::{
    IResult, Parser,
    bytes::complete::tag,
    character::complete::{digit1, space0},
    sequence::separated_pair,
    combinator::map_res,
};

fn number(input: &str) -> IResult<&str, u32> {
    map_res(digit1, str::parse).parse(input)
}

// Parses "12x34" into (12, 34)
fn dimensions(input: &str) -> IResult<&str, (u32, u32)> {
    separated_pair(number, tag("x"), number).parse(input)
}

let (rest, (w, h)) = dimensions("1920x1080")?;
Each parser returns what it did not consume, which is how they chain. `number` is testable in isolation, and the grammar reads like its own description.

Advanced usage

Where the library earns its place over a simpler alternative.

Alternatives, repetition and errors
use nom::{branch::alt, multi::separated_list0, sequence::delimited};

#[derive(Debug)]
enum Value { Num(f64), Text(String), List(Vec<Value>) }

fn value(input: &str) -> IResult<&str, Value> {
    alt((
        map(double, Value::Num),
        map(quoted_string, Value::Text),
        map(list, Value::List),
    )).parse(input)
}

fn list(input: &str) -> IResult<&str, Vec<Value>> {
    delimited(
        (tag("["), space0),
        separated_list0((tag(","), space0), value),
        (space0, tag("]")),
    ).parse(input)
}

// For readable failures, use VerboseError and convert_error.
alt tries each branch in order and takes the first success, which makes it easy to write a recursive grammar. Order the alternatives so the most specific comes first.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Parser succeeds but leaves input unconsumed
Wrap the top-level parser in all_consuming so trailing junk is an error.
Compile errors after upgrading
nom 8 moved to a Parser trait with .parse(); the old function-call style no longer compiles. Check the version an example targets.

Best practices

  • Build small named parsers and unit-test them individually.
  • Use VerboseError plus convert_error for diagnostics; the default error type is terse by design.
  • Order alt branches from most to least specific.
  • Check that all input was consumed — a parser succeeding on a prefix is a common oversight.

Background

Why it exists, and what it was reacting to.

nom is used wherever Rust needs to parse a format — network protocols, file headers, configuration languages. Combinators mean each piece is independently testable rather than one large state machine.