parsing - 将闭包作为参数并返回值,Fn或FnMut是否更惯用?

标签 parsing rust closures mutable boundary

继续How do I write combinators for my own parsers in Rust?,我偶然发现了有关使用和/或产生函数/闭包的函数范围的问题。

通过这些slides,我了解到为了方便消费者,您应该尝试将函数作为FnOnce并在可能的情况下作为Fn返回。这为调用者提供了最大的自由,可以传递什么以及如何使用返回的函数。

在我的示例中,FnOnce是不可能的,因为我需要多次调用该函数。在尝试使其编译时,我得出了两种可能性:

pub enum Parsed<'a, T> {
    Some(T, &'a str),
    None(&'a str),
}

impl<'a, T> Parsed<'a, T> {
    pub fn unwrap(self) -> (T, &'a str) {
        match self {
            Parsed::Some(head, tail) => (head, &tail),
            _ => panic!("Called unwrap on nothing."),
        }
    }

    pub fn is_none(&self) -> bool {
        match self {
            Parsed::None(_) => true,
            _ => false,
        }
    }
}

pub fn achar(character: char) -> impl Fn(&str) -> Parsed<char> {
    move |input|
        match input.chars().next() {
            Some(c) if c == character => Parsed::Some(c, &input[1..]),
            _ => Parsed::None(input),
        }
}

pub fn some_v1<T>(parser: impl Fn(&str) -> Parsed<T>) -> impl Fn(&str) -> Parsed<Vec<T>> {
    move |input| {
        let mut re = Vec::new();
        let mut pos = input;
        loop {
            match parser(pos) {
                Parsed::Some(head, tail) => {
                    re.push(head);
                    pos = tail;
                }
                Parsed::None(_) => break,
            }
        }
        Parsed::Some(re, pos)
    }
}

pub fn some_v2<T>(mut parser: impl FnMut(&str) -> Parsed<T>) -> impl FnMut(&str) -> Parsed<Vec<T>> {
    move |input| {
        let mut re = Vec::new();
        let mut pos = input;
        loop {
            match parser(pos) {
                Parsed::Some(head, tail) => {
                    re.push(head);
                    pos = tail;
                }
                Parsed::None(_) => break,
            }
        }
        Parsed::Some(re, pos)
    }
}

#[test]
fn try_it() {
    assert_eq!(some_v1(achar('#'))("##comment").unwrap(), (vec!['#', '#'], "comment"));
    assert_eq!(some_v2(achar('#'))("##comment").unwrap(), (vec!['#', '#'], "comment"));
}

playground

现在我不知道哪个版本是首选。版本1采用了较不通用的Fn,但版本2需要将其参数可变。

哪个更惯用/应该使用?其背后的原理是什么?

更新:感谢jplatte对版本1的建议。我在这里更新了代码,这种情况我觉得更加有趣。

最佳答案

在编写some_v1some_v2时将它们进行比较,我会说绝对应该首选版本2,因为它更通用。我想不出一个可以实现FnMut而不是Fn的解析闭包的好例子,但是parser成为mut确实没有缺点-正如您对问题的第一条评论中所述,这不会以任何方式限制调用者。

但是,有一种方法可以使版本1比版本2更通用(不是严格更通用,只是部分),即通过返回impl Fn(&str) -> …而不是impl FnMut(&str) -> …。这样,您将获得两个函数,每个函数在某种程度上都比另一个函数受到更少的约束,因此保留这两个函数甚至可能是有意义的:

带有返回类型更改的

  • 版本1对其参数的限制更严格(可调用对象不能变异其关联数据),但对其返回类型的限制较小(您保证返回的可调用对象不会对其相关数据进行变异)
  • 第2版在其参数上的限制较少(允许可调用对象改变其关联数据),但在其返回类型上具有更多限制(返回的可调用对象可能会改变其关联数据)

  • 关于parsing - 将闭包作为参数并返回值,Fn或FnMut是否更惯用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60238608/

    相关文章:

    javascript - 有人可以解释闭包的 js 变量提升和堆栈执行吗

    algorithm - 使用子树查找相似的代码段

    python - 为什么在解析 JSON 时应该首选 json.loads 而不是 ast.literal_eval?

    rust - Rust中具有HashMap拥有的节点的链接列表

    swift - Swift 中的属性闭包和方法有什么区别?

    Java 闭包类型、变量、数组和集合

    parsing - Happy 中的解析器

    python - 解析具有各种特殊字符的消息并拆分为列表(re 和 regex)Python 2.7

    rust - 如何使用 `index_mut` 获取可变引用?

    rust - 返回 `impl Fn` 时令人困惑的生命周期问题