0

假设我想创建一个组合器,它多次使用另一个解析器,例如,解析由两种引号分隔的字符串:

fn quoted<'a, F: 'a, O, E: ParseError<&'a str>>(f: F) -> impl Fn(&'a str) -> IResult<&'a str, O, E>
where
    F: Fn(&'a str) -> IResult<&'a str, O, E>,
{
    map(
        alt((
            tuple((tag("'"), f, tag("'"))),
            tuple((tag("\""), f, tag("\"")))
        )),
        |(_, res, _)| res,
    )
}

正如预期的那样,此解析器无法编译并出现“使用移动值”错误:

149 |     fn quoted<'a, F: 'a, O, E: ParseError<&'a str>>(f: F) -> impl Fn(&'a str) -> IResult<&'a str, O, E>
    |                   -                                 - move occurs because `f` has type `F`, which does not implement the `Copy` trait
    |                   |
    |                   consider adding a `Copy` constraint to this type argument
...
155 |                 tuple((tag("'"), f, tag("'"))),
    |                                  - value moved here
156 |                 tuple((tag("\""), f, tag("\"")))
    |                                   ^ value used here after move

但是,我不能只添加CopyorCloneF边界:很多解析器,特别是由 Nom 的内置函数返回的解析器,既不实现Clone也不实现Copy。我也不能&f用作 的参数tuple,因为那将是一个借用检查错误(f是一个临时的本地值,因此不可能返回用它构造的解析器)。

我看到这样做的唯一方法实际上是alt直接在函数中重新实现逻辑,通过在一系列嵌套match语句中展开它,但这似乎真的不是最理想的。或者也许我错过了一些简单的东西,实际上可以只使用组合器来做我想做的事情?

我很确定有更好的方法来专门编写quoted如上所述的组合器,如果有人展示它会很好,但我的问题更笼统 - 我如何编写重用相同解析器的组合器?

4

1 回答 1

4

最简单的方法是明确返回的闭包:

fn quoted<'a, F: 'a, O, E: ParseError<&'a str>>(f: F) -> impl Fn(&'a str) -> IResult<&'a str, O, E>
where
    F: Fn(&'a str) -> IResult<&'a str, O, E>,
{
    move |i| {
        map(
            alt((
                tuple((tag("'"), &f, tag("'"))),
                tuple((tag("\""), &f, tag("\"")))
            )),
            |(_, res, _)| res,
        )(i)
    }
}

现在,由于move关键字,f值被移动到闭包中。然后,在返回的闭包中,我直接调用复杂的解析器组合器,除了输出/错误之外什么都不会从闭包返回,这意味着我可以自由使用对f.

于 2019-07-02T05:53:48.540 回答