Skip to content

Guide · Open source

How to use nom: a Rust parser combinator guide

Updated · written with AI from the project's own documentation and checked against our catalogue data

nom is an open-source parser combinators library written in Rust, licensed under MIT. It lets you build parsers by assembling small functions, such as 'take 5 bytes' or 'recognize the word HTTP', into larger patterns that read like the grammar they describe. This guide covers installing nom, the core idea behind combinators, a complete working example, and when nom is the right choice.

nomrust-bakery/nom · ★ 10.5k · MIT

Quick start

  1. 1Check your Rust toolchain. Confirm you have rustc 1.65 or newer, the minimum version the README lists for nom.
  2. 2Add nom to your project. Run cargo add nom from the root of your Cargo project.
  3. 3Read the reference docs. Open docs.rs/nom to see the available functions, macros and traits.
  4. 4Copy the hex color example. Put the README's hex color parser into a Rust file so you have a working starting point.
  5. 5Run the test. Run the parse_color test to confirm #2F14DF parses to red 47, green 20 and blue 223.
  6. 6Build your own combinators. Write small parsers for single pieces of your format and combine them into larger ones.

What is nom and who is it for?

nom is a parser combinators library written in Rust. Its goal is to provide tools to build safe parsers without compromising speed or memory consumption. It uses Rust's strong typing and memory safety, and provides functions, macros and traits that abstract most of the error-prone plumbing.

It is designed for reading binary formats, text formats, and streaming data without copying input unnecessarily. Best suited to Rust developers writing parsers for file formats, developers parsing network protocols or data that arrives in chunks, and engineers prototyping a parser for a language or data format.

Install nom in a Rust project

nom is installed as a dependency of a Rust project. You need a Rust toolchain; the README states nom's MSRV as rustc 1.65 or newer. Add nom to your project with the command below.

  • Make sure your Rust toolchain meets the minimum version (rustc 1.65 or newer).
  • Run the command from the root of your Cargo project.
cargo add nom

Understand parser combinators

Parser combinators differ from tools like lex and yacc. Instead of writing the grammar in a separate file and generating code, you write very small functions with a specific purpose, then combine them. For example, you might combine 'recognize HTTP', then a space, then a version.

The resulting code is small and looks like the grammar you would have written with other approaches. Because each piece is separate, parsers are easier to test and reuse.

  • The parsers are small and easy to write.
  • Components are easy to reuse and test separately, with unit tests and property-based tests.
  • You can build partial parsers that handle only the data you need and ignore the rest.

Read a complete nom example: a hex color parser

The README includes a parser for hexadecimal colors such as #2F14DF. The hex_primary function reads two hex digits and converts them to a u8. The hex_color function matches the leading # tag, then parses three primary values into a Color struct.

The example ends with a test that checks #2F14DF parses to red 47, green 20 and blue 223. Run it as a standard Rust test to confirm your setup works.

  • tag matches a fixed string such as #.
  • take_while_m_n takes between a minimum and maximum number of characters that match a predicate.
  • map_res converts the matched text, here with u8::from_str_radix.
  • Tuples of parsers, combined with .parse(input), run parsers in sequence.
use nom::{ bytes::complete::{tag, take_while_m_n}, combinator::map_res, sequence::Tuple, IResult, Parser, }; #[derive(Debug, PartialEq)] pub struct Color { pub red: u8, pub green: u8, pub blue: u8, } fn from_hex(input: &str) -> Result<u8, std::num::ParseIntError> { u8::from_str_radix(input, 16) } fn is_hex_digit(c: char) -> bool { c.is_digit(16) } fn hex_primary(input: &str) -> IResult<&str, u8> { map_res( take_while_m_n(2, 2, is_hex_digit), from_hex ).parse(input) } fn hex_color(input: &str) -> IResult<&str, Color> { let (input, _) = tag("#")(input)?; let (input, (red, green, blue)) = (hex_primary, hex_primary, hex_primary).parse(input)?; Ok((input, Color { red, green, blue })) } fn main() { println!("{:?}", hex_color("#2F14DF")) } #[test] fn parse_color() { assert_eq!( hex_color("#2F14DF"), Ok(( "", Color { red: 47, green: 20, blue: 223, } )) ); }

Choose the right kind of parser for your data

nom covers several data types. For binary formats, the basic type is a byte slice and parsers work on &[u8]. Bit-level parsing lets you address a byte slice as a bit stream. For text, the same combinators apply to UTF-8 strings.

For data that arrives in parts, such as network formats or huge files, nom can detect when it needs more input instead of returning a wrong result. The README notes that whether data comes entirely or in chunks, the result should be the same.

  • Binary formats: byte-oriented parsing, TLV patterns and bit-level parsing.
  • Text formats: CSV, JSON-like nested formats, escaped strings and case-insensitive comparison.
  • Streaming: partial data handling for protocols and large files.
  • Errors: aggregated error lists you can pattern match, plus custom error types.

Find documentation and get help

The reference documentation is hosted on docs.rs/nom. The README also links to The Nominomicon, a guide to using nom, and to a list of combinators and their behaviour in the repository's doc folder.

For help, the README points to the Gitter chat room and to the #nom-parsers channel on Libera IRC.

  • Reference documentation: docs.rs/nom
  • The Nominomicon: a guide to using nom
  • List of combinators and their behaviour in the doc folder of the repository

When nom is not the right tool

nom is a Rust-only library. If you are not writing Rust, it will not help you. It also is not a generator: you do not write a grammar file and produce a parser from it. You write the parser by hand with combinators.

Beginners new to Rust may find the examples hard going, because they rely on Rust's type system and error handling. If you need a ready-made tool that reads a grammar file, look at other options, such as pest, which is listed as a comparable project.

What people use it for

  • Parsing binary file formats such as FLV or Matroska, as shown by example projects in the README.
  • Parsing text formats such as CSV, JSON or TOML.
  • Parsing network protocols or data arriving in chunks, using nom's streaming support.
  • Prototyping a parser for a programming language and attaching line and column information with nom_locate.

Frequently asked questions

Is nom free to use?

Yes. nom is licensed under the MIT licence, according to the project's license badge in the README. Check the LICENSE file in the repository for the full terms.

How do I install nom?

Run cargo add nom in the root of your Rust project. You need a Rust toolchain of rustc 1.65 or newer, which the README gives as the minimum supported version.

What do I need to use nom?

You need Rust and a Cargo project. nom is a Rust-only library, so it is not usable from other languages.

Is nom a parser generator?

No. nom is a parser combinators library. You write parsers by hand by combining small functions, rather than writing a grammar file and generating code from it.

How does nom compare to pest or Parsec?

The README lists Parsec, attoparsec, pest and nom_locate as comparable projects and says benchmarks have shown nom often outperforms Parsec, attoparsec and some regex engines. The benchmarks are in the parser_benchmarks repository. Pest, by contrast, is driven by a grammar file, which nom is not.

Can nom parse data that arrives in chunks?

Yes. nom has streaming support. If there is not enough data to decide, it reports that it needs more instead of returning a wrong result, which is useful for network protocols and huge files.

New agent skills, repos and AI tools — every week

The best new skills and open-source projects, the fastest-growing repos, and one free prompt. Five minutes to read. Unsubscribe anytime.

Our verdict

Use nom if you write Rust and need small, testable, reusable parsers for binary, text or streaming data, and you are comfortable writing code by hand. Pick something else if you are not using Rust, want to generate a parser from a grammar file, or are still learning Rust, since the examples assume familiarity with its type system.

Install options & downloads