Loading pageā¦
Rust walkthroughs
Loading pageā¦
nom::branch::alt differ from nom::combinator::or for handling multiple parser alternatives?nom::branch::alt accepts a tuple of parsers and tries each in sequence until one succeeds, returning early on the first match. nom::combinator::or is a binary combinator that tries exactly two parsersātrying the first and falling back to the second if it fails. The key difference is that alt is designed for multiple alternatives with optimized error accumulation, while or is for simple two-way choices. For more than two alternatives, alt is preferred because or chains create nested error types and less efficient backtracking, whereas alt accumulates errors from all attempted parsers to provide better error messages.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::IResult;
fn main() {
// alt tries each parser in order until one succeeds
let parser = alt((
tag("hello"),
tag("world"),
tag("foo"),
));
// Try "hello" first
let result: IResult<&str, &str> = parser("hello world");
println!("{:?}", result); // Ok((" world", "hello"))
// "hello" fails, try "world"
let result2: IResult<&str, &str> = parser("world!");
println!("{:?}", result2); // Ok(("!", "world"))
// "hello" and "world" fail, try "foo"
let result3: IResult<&str, &str> = parser("foobar");
println!("{:?}", result3); // Ok(("bar", "foo"))
// All fail
let result4: IResult<&str, &str> = parser("baz");
println!("{:?}", result4); // Err(Error)
}alt takes a tuple of parsers and tries them in sequence.
use nom::combinator::or;
use nom::bytes::complete::tag;
use nom::IResult;
fn main() {
// or is binary - only two parsers
let parser = or(tag("hello"), tag("world"));
let result: IResult<&str, &str> = parser("hello!");
println!("{:?}", result); // Ok(("!", "hello"))
let result2: IResult<&str, &str> = parser("world!");
println!("{:?}", result2); // Ok(("!", "world"))
// or returns the error from the last parser tried
let result3: IResult<&str, &str> = parser("foo");
println!("{:?}", result3); // Err(Error from "world")
}or is a binary operator for exactly two parsers.
use nom::combinator::or;
use nom::bytes::complete::tag;
use nom::IResult;
fn main() {
// Chaining or for multiple alternatives - verbose and inefficient
let parser = or(
tag("hello"),
or(
tag("world"),
or(
tag("foo"),
tag("bar")
)
)
);
// This works but creates nested error types
let result: IResult<&str, &str> = parser("foo!");
println!("{:?}", result); // Ok(("!", "foo"))
// Each nested or wraps the error type further
// Error reporting becomes complex
}Chaining or works but creates nested structures and error types.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::IResult;
fn main() {
// alt handles multiple parsers cleanly
let parser = alt((
tag("if"),
tag("else"),
tag("for"),
tag("while"),
tag("return"),
tag("break"),
));
// Tries each in order
assert!(parser("if x").is_ok());
assert!(parser("else {").is_ok());
assert!(parser("for i").is_ok());
assert!(parser("while true").is_ok());
// Clean error message when all fail
let result = parser("unknown");
println!("Error: {:?}", result);
}alt cleanly handles any number of parsers in a tuple.
use nom::branch::alt;
use nom::combinator::or;
use nom::bytes::complete::tag;
use nom::error::{Error, ParseError};
use nom::IResult;
fn main() {
// alt accumulates errors from all parsers
let alt_parser = alt((
tag("hello"),
tag("world"),
tag("foo"),
));
let alt_result = alt_parser("baz");
println!("alt error: {:?}", alt_result);
// Error includes context about what was tried
// or returns error from the last parser
let or_parser = or(tag("hello"), tag("world"));
let or_result = or_parser("baz");
println!("or error: {:?}", or_result);
// Error only from "world" (the second parser)
}alt can provide better error messages by accumulating errors from all alternatives.
use nom::branch::alt;
use nom::combinator::or;
use nom::bytes::complete::tag;
fn main() {
// alt is optimized for multiple alternatives
// - Single pass through alternatives
// - Early return on success
// - Efficient error accumulation
let alt_parser = alt((
tag("if"),
tag("else"),
tag("for"),
tag("while"),
));
// or chains create nested structures
// - Each or is a separate function call
// - Nested error types
// - More stack frames
let or_parser = or(
tag("if"),
or(
tag("else"),
or(
tag("for"),
tag("while")
)
)
);
// For 2 alternatives: or is fine
// For 3+ alternatives: alt is preferred
}alt is optimized for multiple alternatives; or chains have overhead.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::alpha1;
use nom::sequence::preceded;
use nom::IResult;
#[derive(Debug)]
enum Keyword {
If,
Else,
For,
While,
Return,
}
fn parse_keyword(input: &str) -> IResult<&str, Keyword> {
// alt with mapped results
alt((
|i| tag("if")(i).map(|(r, _)| (r, Keyword::If)),
|i| tag("else")(i).map(|(r, _)| (r, Keyword::Else)),
|i| tag("for")(i).map(|(r, _)| (r, Keyword::For)),
|i| tag("while")(i).map(|(r, _)| (r, Keyword::While)),
|i| tag("return")(i).map(|(r, _)| (r, Keyword::Return)),
))(input)
}
fn main() {
assert_eq!(parse_keyword("if x"), Ok((" x", Keyword::If)));
assert_eq!(parse_keyword("else {"), Ok((" {", Keyword::Else)));
assert_eq!(parse_keyword("for i"), Ok((" i", Keyword::For)));
}alt works well with mapped results for parsing into types.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::map;
use nom::IResult;
#[derive(Debug, PartialEq)]
enum Token {
Add,
Sub,
Mul,
Div,
}
fn parse_operator(input: &str) -> IResult<&str, Token> {
// Combine alt with map for clean transformations
alt((
map(tag("+"), |_| Token::Add),
map(tag("-"), |_| Token::Sub),
map(tag("*"), |_| Token::Mul),
map(tag("/"), |_| Token::Div),
))(input)
}
fn main() {
assert_eq!(parse_operator("+ rest"), Ok((" rest", Token::Add)));
assert_eq!(parse_operator("- rest"), Ok((" rest", Token::Sub)));
assert_eq!(parse_operator("* rest"), Ok((" rest", Token::Mul)));
assert_eq!(parse_operator("/ rest"), Ok((" rest", Token::Div)));
assert!(parse_operator("% rest").is_err());
}alt combines cleanly with map for parsing into custom types.
use nom::combinator::or;
use nom::bytes::complete::tag;
use nom::character::complete::digit1;
use nom::IResult;
fn main() {
// For simple two-way choices, or is perfectly fine
let string_or_number = or(
tag("\""),
digit1
);
// This is clearer than alt for just two options
let result1 = string_or_number("\"hello");
println!("{:?}", result1); // Ok(("hello", "\""))
let result2 = string_or_number("12345");
println!("{:?}", result2); // Ok(("", "12345"))
// or reads naturally for binary choices:
// "try this OR that"
}or is appropriate for simple two-way choices where clarity matters.
use nom::branch::alt;
use nom::bytes::complete::{tag, take_while};
use nom::character::complete::{digit1, alpha1};
use nom::sequence::delimited;
use nom::IResult;
#[derive(Debug)]
enum Literal {
String(String),
Number(i64),
Boolean(bool),
}
fn parse_literal(input: &str) -> IResult<&str, Literal> {
alt((
// String literal
|i| {
delimited(tag("\""), take_while(|c| c != '"'), tag("\""))(i)
.map(|(r, s)| (r, Literal::String(s.to_string())))
},
// Boolean literals
|i| tag("true")(i).map(|(r, _)| (r, Literal::Boolean(true))),
|i| tag("false")(i).map(|(r, _)| (r, Literal::Boolean(false))),
// Number literal
|i| digit1(i).map(|(r, n)| (r, Literal::Number(n.parse().unwrap()))),
))(input)
}
fn main() {
assert!(matches!(parse_literal("\"hello\""), Ok((_, Literal::String(_)))));
assert!(matches!(parse_literal("42"), Ok((_, Literal::Number(42)))));
assert!(matches!(parse_literal("true"), Ok((_, Literal::Boolean(true)))));
}alt handles complex nested parsers with different return types.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::error::{VerboseError, VerboseErrorKind};
use nom::IResult;
fn main() {
// With VerboseError, alt accumulates context from all failures
let parser = alt((
tag("hello"),
tag("world"),
tag("foo"),
));
let input = "unknown";
// Error will show all attempted alternatives
let result: IResult<&str, &str, VerboseError<&str>> = parser(input);
match result {
Err(nom::Err::Error(e)) => {
println!("Error context accumulated from all alternatives");
// VerboseError contains errors from each parser tried
}
_ => {}
}
}With verbose error types, alt can provide richer error messages from all attempts.
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{digit1, space0};
use nom::sequence::{pair, delimited};
use nom::combinator::map;
use nom::IResult;
#[derive(Debug)]
enum Expr {
Number(i64),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Paren(Box<Expr>),
}
fn parse_number(input: &str) -> IResult<&str, Expr> {
map(digit1, |s: &str| Expr::Number(s.parse().unwrap()))(input)
}
fn parse_parens(input: &str) -> IResult<&str, Expr> {
map(
delimited(tag("("), parse_expr, tag(")")),
|e| Expr::Paren(Box::new(e))
)(input)
}
fn parse_primary(input: &str) -> IResult<&str, Expr> {
alt((
parse_number,
parse_parens,
))(input)
}
fn parse_expr(input: &str) -> IResult<&str, Expr> {
// Simplified - just showing alt usage
parse_primary(input)
}
fn main() {
let result = parse_primary("42");
println!("{:?}", result); // Ok(("", Number(42)))
let result2 = parse_primary("(1)");
println!("{:?}", result2); // Ok(("", Paren(Number(1))))
}alt is essential for expression parsers with multiple alternatives.
use nom::branch::alt;
use nom::combinator::or;
use nom::bytes::complete::tag;
fn main() {
// Summary of differences:
// alt:
// - Takes tuple of parsers (2 or more)
// - Optimized for multiple alternatives
// - Accumulates errors from all parsers
// - Clean syntax for many alternatives
// - Preferred for 3+ alternatives
let alt_parser = alt((
tag("a"),
tag("b"),
tag("c"),
tag("d"),
));
// or:
// - Takes exactly 2 parsers
// - Binary choice
// - Returns error from last parser
// - Can chain but creates nesting
// - Fine for simple two-way choices
let or_parser = or(tag("a"), tag("b"));
// For two parsers: both work, or is slightly simpler
// For 3+ parsers: alt is cleaner and more efficient
// Chained or (avoid for many alternatives):
let chain = or(tag("a"), or(tag("b"), tag("c")));
// Works but less readable than alt
}The key difference: alt for multiple alternatives, or for binary choices.
Core distinction:
alt: Tuple of parsers, tries each in sequence, returns first successor: Binary combinator, tries first then secondError handling:
alt: Can accumulate errors from all attempted parsersor: Returns error from the last parser triedVerboseError with alt for better messagesPerformance:
alt: Optimized for multiple alternatives, single pass structureor: Chaining creates nested calls and error type nestingalt preferredWhen to use alt:
When to use or:
Common patterns:
alt + map: Transform multiple alternatives into typesalt for parsing expressions with multiple alternativesalt for recursive grammarsKey insight: Both combinators express "try alternatives until one succeeds," but alt generalizes this to any number of parsers while or is strictly binary. The alt combinator is more than syntactic sugarāit's optimized for the multi-alternative case with better error accumulation and cleaner error types. Chaining or works but creates increasingly nested error types (like Result<Result<Result<...>>>>), making error handling complex. Use alt whenever you have more than two alternatives; use or for simple two-way choices where the binary nature reads clearly in context.