为什么我不能移动由特征表示的闭包?

问题描述 投票:0回答:1

我正在尝试实现一些解析组合器。每个解析器组合器都是一个可变的闭包,但外部代码将每个组合器称为一个特征,该特征为调用组合器函数提供了一个全面的实现。

// Region of external code, CANNOT modify

type ParseResult<I, T> = Result<(I, T), String>;

trait Parser<T> {
    fn parse<'a>(&mut self, input: &'a str) -> ParseResult<&'a str, T>;
}

// Blanket implementation for parser combinators
impl<F, T> Parser<T> for F
where F: FnMut(&str) -> ParseResult<&str, T> {
    fn parse<'a>(&mut self, input: &'a str) -> ParseResult<&'a str, T> {
        self(input)
    }
}

// Example of an external combinator
fn external_combinator<P, T>(parser: P) -> impl FnMut(&str) -> ParseResult<&str, T>
where P: Parser<T> {
    preceded_by(char_parser('a'), parser)
}

// My code begins here

fn char_parser(c: char) -> impl Fn(&str) -> ParseResult<&str, char> {
    move |input: &str| {
        let Some(rest) = input.strip_prefix(c) else {
            return Err(format!("Input does not begin with {c}"));
        };
        Ok((rest, c))
    }
}

fn delimited_by<Extracted>(
    begin_char: char,
    final_char: char,
    mut parser: impl Parser<Extracted>
) -> impl FnMut(&str) -> ParseResult<&str, Extracted> {
    move |input: &str| {
        let parse_begin = char_parser(begin_char);

        // Why cannot I move parser to preceded_by?          <-- ERROR HERE
        let precede = preceded_by(parse_begin, &mut parser);

        let parse_final = char_parser(final_char);
        followed_by(precede, parse_final)(input)
    }
}

fn followed_by<Extracted, _Discard>(
    mut parser_a: impl Parser<Extracted>,
    mut parser_b: impl Parser<_Discard>
) -> impl FnMut(&str) -> ParseResult<&str, Extracted> {
    move |input: &str| {
        let (rest, new_value) = parser_a.parse(input)?;
        let (rest, _) = parser_b.parse(rest)?;
        Ok((rest, new_value))
    }
}

fn preceded_by<Extracted, _Discard>(
    mut parser_a: impl Parser<_Discard>,
    mut parser_b: impl Parser<Extracted>
) -> impl FnMut(&str) -> ParseResult<&str, Extracted> {
    move |input: &str| {
        let (rest, _) = parser_a.parse(input)?;
        parser_b.parse(rest)
    }
}

// Region of external code, cannot modify
fn main() {
    let my_combinator = delimited_by('a', 'b', char_parser('c'));
    let mut final_combinator = external_combinator(my_combinator);

    match final_combinator("data") {
        Ok(_) => println!("OK"),
        Err(_) => println!("Error")
    }
}

当我尝试编译此演示时,出现以下错误。

error[E0277]: the trait bound `&mut impl Parser<Extracted>: Parser<_>` is not satisfied
  --> src/main.rs:43:48
   |
43 |         let precede = preceded_by(parse_begin, &mut parser);
   |                       -----------              ^^^^^^^^^^^ the trait `for<'a> FnMut(&'a str)` is not implemented for `impl Parser<Extracted>`, which is required by `&mut impl Parser<Extracted>: Parser<_>`
   |                       |
   |                       required by a bound introduced by this call
   |
   = note: required for `&mut impl Parser<Extracted>` to implement `for<'a> FnOnce(&'a str)`
note: required for `&mut impl Parser<Extracted>` to implement `Parser<_>`
  --> src/main.rs:10:12
   |
10 | impl<F, T> Parser<T> for F
   |            ^^^^^^^^^     ^
11 | where F: FnMut(&str) -> ParseResult<&str, T> {
   |                         -------------------- unsatisfied trait bound introduced here
note: required by a bound in `preceded_by`
  --> src/main.rs:63:24
   |
61 | fn preceded_by<Extracted, _Discard>(
   |    ----------- required by a bound in this function
62 |     mut parser_a: impl Parser<_Discard>,
63 |     mut parser_b: impl Parser<Extracted>
   |                        ^^^^^^^^^^^^^^^^^ required by this bound in `preceded_by`
help: consider further restricting this bound
   |
37 |     mut parser: impl Parser<Extracted> + for<'a> FnMut(&'a str)
   |                                        ++++++++++++++++++++++++

我尝试应用

+ for<'a> FnMut(&'a str)
但它会导致更多错误,最后编译器希望我更改
Parser<T>
但我不能这样做,因为在生产中
Parser<T>
是由外部代码提供的。

我有几个问题:

  • 如何在不使用
    parser
    delimited_by
    的情况下将参数
    preceded_by
    dyn
    传递到
    Box
  • 如果我不能,我可以重写我的代码来满足
    external_combinator
    中的特征吗?请记住,我无法更改外部区域中任何内容的签名或实现。
  • 如果您至少有一个更适合初学者的解释来解释为什么我无法传递参数
    parser
    ,请发布它,这也会有帮助。
rust closures lifetime parser-combinators
1个回答
0
投票

首先,请注意,在

delimited_by
中,
&mut (impl SomeTrait)
并未实现
SomeTrait
。我们的第一个修改是删除
mut
&mut
:

fn delimited_by<Extracted>(
    begin_char: char,
    final_char: char,
    parser: impl Parser<Extracted> // removed mut
) -> impl FnMut(&str) -> ParseResult<&str, Extracted> {
    move |input: &str| {
        let parse_begin = char_parser(begin_char);

        let precede = preceded_by(parse_begin, parser); // removed &mut

        let parse_final = char_parser(final_char);
        followed_by(precede, parse_final)(input)
    }
}

现在,我们遇到了第二个问题。当函数采用

impl SomeTrait
时(在本例中为
impl Parser<Extracted>
),Rust 默认假设没有额外的特征,如
Copy
Clone
。这意味着
parser
不能分别隐式或显式复制。然而,由于
delimited_by
返回一个每次执行时都需要
parser
的闭包,因此我们确实需要
parser
的多个副本或克隆。

我们可以添加一个

Clone
绑定到参数,这样我们就可以在每次闭包运行时
clone
它。我们还必须明确地说
char_parser
返回一个
Clone
able 函数。

fn char_parser(c: char) -> impl Fn(&str) -> ParseResult<&str, char> + Clone // Add Clone {
    move |input: &str| {
        let Some(rest) = input.strip_prefix(c) else {
            return Err(format!("Input does not begin with {c}"));
        };
        Ok((rest, c))
    }
}

fn delimited_by<Extracted>(
    begin_char: char,
    final_char: char,
    parser: impl Parser<Extracted> + Clone // Add Clone
) -> impl FnMut(&str) -> ParseResult<&str, Extracted> {
    move |input: &str| {
        let parse_begin = char_parser(begin_char);

        let precede = preceded_by(parse_begin, parser.clone()); // Add clone

        let parse_final = char_parser(final_char);
        followed_by(precede, parse_final)(input)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.