继续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_v1
和some_v2
时将它们进行比较,我会说绝对应该首选版本2,因为它更通用。我想不出一个可以实现FnMut
而不是Fn
的解析闭包的好例子,但是parser
成为mut
确实没有缺点-正如您对问题的第一条评论中所述,这不会以任何方式限制调用者。
但是,有一种方法可以使版本1比版本2更通用(不是严格更通用,只是部分),即通过返回impl Fn(&str) -> …
而不是impl FnMut(&str) -> …
。这样,您将获得两个函数,每个函数在某种程度上都比另一个函数受到更少的约束,因此保留这两个函数甚至可能是有意义的:
带有返回类型更改的
关于parsing - 将闭包作为参数并返回值,Fn或FnMut是否更惯用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60238608/