Parsing 使用闭包作为参数和返回值,Fn或FnMut更惯用吗?

Parsing 使用闭包作为参数和返回值,Fn或FnMut更惯用吗?,parsing,rust,closures,mutable,boundary,Parsing,Rust,Closures,Mutable,Boundary,接着,我偶然发现了一个问题,关于使用和/或产生函数/闭包的函数的边界 从中,我了解到为了方便消费者,您应该尝试将函数作为Fn一次,并尽可能以Fn返回。这使调用者可以自由地传递什么以及如何处理返回的函数 在我的示例中,FnOnce是不可能的,因为我需要多次调用该函数。在尝试编译时,我发现了两种可能性: pub enum已解析(head和tail), _=>恐慌!(“称为“无条件展开”), } } pub fn是\u none(&self)->bool{ 匹配自我{ 已解析::无()=>true,

接着,我偶然发现了一个问题,关于使用和/或产生函数/闭包的函数的边界

从中,我了解到为了方便消费者,您应该尝试将函数作为
Fn一次
,并尽可能以
Fn
返回。这使调用者可以自由地传递什么以及如何处理返回的函数

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

pub enum已解析(head和tail),
_=>恐慌!(“称为“无条件展开”),
}
}
pub fn是\u none(&self)->bool{
匹配自我{
已解析::无()=>true,
_=>错误,
}
}
}
pub-fn-achar(字符:char)->impl-fn(&str)->已解析{
移动|输入|
匹配input.chars().next(){
Some(c)如果c==character=>Parsed::Some(c,&input[1..]),
_=>已解析::无(输入),
}
}
pub fn some_v1(解析器:impl fn(&str)->Parsed)->impl fn(&str)->Parsed{
移动|输入|{
让mut re=Vec::new();
让mut pos=输入;
环路{
匹配分析器(pos){
解析::一些(头、尾)=>{
再推(头);
pos=尾部;
}
已解析::无()=>中断,
}
}
解析::一些(re,pos)
}
}
pub fn some_v2(mut解析器:impl FnMut(&str)->已解析)->impl FnMut(&str)->已解析{
移动|输入|{
让mut re=Vec::new();
让mut pos=输入;
环路{
匹配分析器(pos){
解析::一些(头、尾)=>{
再推(头);
pos=尾部;
}
已解析::无()=>中断,
}
}
解析::一些(re,pos)
}
}
#[测试]
fn try_it(){
assert_eq!(一些_v1(achar(“#”)(“###comment”).unwrap(),(vec![“#”,“#”],“comment”);
assert_eq!(一些_v2(achar(“#”)(“###comment”).unwrap(),(vec![“#”,“#”],“comment”);
}

现在我不知道哪个版本更可取。版本1采用的是
Fn
,它不太通用,但版本2需要其参数可变

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



更新:感谢您对第一版的建议。我在这里更新了代码,我发现这种情况更有趣。

在您编写代码时,比较
一些v1
一些v2
,我想说版本2肯定是首选的,因为它更通用。我想不出一个解析闭包的好例子,它可以实现
FnMut
,但不能实现
Fn
,但是
parser
作为
mut
,确实没有什么不利之处——正如在关于您问题的第一条评论中所指出的,这并不以任何方式约束调用方

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

  • 具有返回类型更改的版本1的参数限制性更强(可调用函数无法更改其关联数据),但其返回类型限制性更小(您可以保证返回的可调用函数不会更改其关联数据)
  • 版本2的参数限制性较小(允许可调用函数变异其关联数据),但其返回类型限制性较大(返回的可调用函数可能变异其关联数据)

将参数绑定为可变参数并不意味着对调用方有任何约束。这与您在函数签名中省略了
mut
,并使用
let mut parser=parser是一样的
作为
some_v2
中的第一行。请参阅和。请参阅更新的问题,谢谢。有了这个变化,版本1似乎更适合组合(希望如此)无状态解析器,对吗?是的,没错。例如,您可以将
一些_v1
传递给自身(这不是特别有用)或类似的组合器(例如
fn(times:usize,parser:impl-fn(&str)->Parsed)->impl-fn(&str)->Parsed
)。