You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在nom的alt组合子中复用无法移动的小型解析器?

解决nom中复用闭包子解析器的移动问题

问题根源

你遇到的问题是:nom组合出来的闭包型解析器默认仅实现FnMut trait,没有实现Clone或Copy,第一次使用时所有权会被移动,后续无法重复调用。

简洁解决方案

方案1:将子解析器定义为独立函数

函数可以被多次调用,每次都会生成新的解析器实例,完全避免所有权移动问题,这也是最常用的解决方案:

use nom::{
    character::complete::{alphanumeric1, tag},
    combinator::{map, recognize},
    multi::many0,
    sequence::separated_pair,
    branch::alt,
    IResult,
};

// 把word解析器定义为函数
fn word(i: &str) -> IResult<&str, &str> {
    recognize(many0(alt((alphanumeric1, tag("_")))))(i)
}

fn parse_dot_sep_words_or_word(i: &str) -> IResult<&str, (&str, &str)> {
    let dot_sep_word = separated_pair(word, tag("."), word);

    alt((
        map(word, |single_word| ("", single_word)),
        map(dot_sep_word, |(a,b)| (a,b))
    ))(i)
}

方案2:用BoxedParser装箱实现复用

如果不想单独定义函数,可以将解析器装箱为BoxedParser,它实现了Clone trait,允许我们复制解析器实例:

use nom::{
    character::complete::{alphanumeric1, tag},
    combinator::{map, recognize},
    multi::many0,
    sequence::separated_pair,
    branch::alt,
    IResult,
    error::ErrorKind,
    Parser,
};

fn parse_dot_sep_words_or_word(i: &str) -> IResult<&str, (&str, &str)> {
    let word = recognize(many0(alt((alphanumeric1, tag("_")))))
        .boxed::<_, (&str, ErrorKind)>();
    
    let dot_sep_word = separated_pair(word.clone(), tag("."), word.clone());

    alt((
        map(word, |single_word| ("", single_word)),
        map(dot_sep_word, |(a,b)| (a,b))
    ))(i)
}

方案3:使用nom的parser!宏

nom提供的parser!宏可以帮我们快速生成可复用的解析器,本质是自动生成函数:

use nom::{
    character::complete::{alphanumeric1, tag},
    combinator::{map, recognize},
    multi::many0,
    sequence::separated_pair,
    branch::alt,
    IResult,
    parser,
};

// 用parser!宏定义可复用的word解析器
parser!(fn word(i: &str) -> IResult<&str, &str> {
    recognize(many0(alt((alphanumeric1, tag("_")))))
});

fn parse_dot_sep_words_or_word(i: &str) -> IResult<&str, (&str, &str)> {
    let dot_sep_word = separated_pair(word, tag("."), word);

    alt((
        map(word, |single_word| ("", single_word)),
        map(dot_sep_word, |(a,b)| (a,b))
    ))(i)
}

内容的提问来源于stack exchange,提问作者exocortex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 00:26:13