以可调用对象作为参数并返回值,哪个约束更符合意识形态?

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

previous Question继续,我偶然发现了下一个有关消耗和/或产生函数/闭包的函数范围的问题。

[我从这些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 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)
    }
}

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的参数需要可变。

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

parsing rust closures mutable boundary
1个回答
0
投票
比较您编写它们时的some_v1some_v2,我会说绝对应该首选版本2,因为它更通用。我想不出一个可以实现FnMut而不是Fn的解析闭包的好例子,但是parser确实没有缺点mut-正如您对问题的第一条评论所述,不会以任何方式限制呼叫者。

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

    具有返回类型更改的版本1在其参数上将更具限制性(可调用对象不能变异其关联的数据),而在其返回类型上具有较少的限制性(您保证返回的可调用对象不会变异其关联的数据)]] >
  • 版本2在其参数上的限制较少(允许可调用对象改变其关联的数据),而在其返回类型上具有更多限制(返回的可调用对象可能会改变其关联的数据)
© www.soinside.com 2019 - 2024. All rights reserved.