带arg a函数的Rust函数
我想编写一个通用函数带arg a函数的Rust函数,rust,function-pointers,Rust,Function Pointers,我想编写一个通用函数count\u调用,它调用一个函数f,它接受一个函数指针(lambda),其中count\u调用计算函数f调用给定lambda函数的频率 我很难接受这种方法() fn count_调用(s:s,f:f)->u32 哪里 S:克隆, F:大小+FnMut(Fn()->S)->(), { 让mut计数器:u32=0; f(| |{ 计数器+=1; s、 克隆() }); 柜台 } #[cfg(测试)] mod stackoverflow{ 使用超级::*; fn f(p:fn()
count\u调用
,它调用一个函数f
,它接受一个函数指针(lambda),其中count\u调用
计算函数f
调用给定lambda函数的频率
我很难接受这种方法()
fn count_调用(s:s,f:f)->u32
哪里
S:克隆,
F:大小+FnMut(Fn()->S)->(),
{
让mut计数器:u32=0;
f(| |{
计数器+=1;
s、 克隆()
});
柜台
}
#[cfg(测试)]
mod stackoverflow{
使用超级::*;
fn f(p:fn()->i32){
p();
p();
}
#[测试]
fn测试(){
let counts=count_调用(3,f);
断言(计数2);
}
}
这里我得到了一个错误:
error[E0277]:编译时无法知道类型为`(dyn std::ops::Fn()->S+'static)`的值的大小
-->src/lib.rs:1:1
|
1 |/fn count_调用(s:s,f:f)->u32
2 | |在哪里
3 | | S:克隆,
4 | F:Sized+FnMut(Fn()->S)->(),
... |
12 | |计数器
13 | | }
|| ^在编译时没有已知的大小
|
=帮助:在“((dyn std::ops::Fn()->S+'static),”)内,`(dyn std::ops::Fn()->S+'static)未实现特性“std::marker::Sized`)`
=注意:要了解更多信息,请访问
=注意:必需,因为它出现在类型`(dyn std::ops::Fn()->S+'static)中)`
=注:std::ops::FnMut需要`
有人知道如何解决这个问题吗
[编辑]
我认为使用
BoxS>
可能是一个解决方案。但如果可能的话,我更喜欢只使用堆栈的解决方案。这是我设法实现的最简单的代码():
“编译时无法知道类型(dyn std::ops::Fn()->S+'static)
的值的大小”错误是由绑定到F
的特征引起的:
F: Sized + FnMut(Fn() -> S) -> ()
这相当于F:Sized+FnMut(dyn Fn()->S)
。这意味着闭包F
将按值获取trait对象(dyn Fn()->S
)。但是trait对象没有大小,并且不能通过值传递
一种解决方案是通过引用或在框中传递trait对象。解释并讨论这些解决方案
我们能避免特征对象和动态调度吗?
我想不太合适
非解决方案
一个想法是将另一个类型参数添加到count\u调用中:
fn count_calls<S, F, G>(s: S, f: F) -> u32
where
S: Clone,
F: Sized + FnMut(G),
G: Fn() -> S,
这里的问题是count\u调用的类型参数是由count\u调用的调用方选择的。但是我们实际上希望G
始终是我们自己的闭包类型。所以这不起作用
我们需要的是一个泛型闭包(在这里我们可以选择它的类型参数)。类似的情况是可能的,但仅限于寿命参数。它被称为HRTBs,看起来像F:fort这比我想象的要难,因为传递给F
的匿名闭包类型。。。我很高兴看到锈专家如何处理这个问题,只是为了确定:这是一个纯粹的理论问题(只是为了好玩/出于兴趣),还是你想解决一个实际问题?如果是后者,也许你想分享一些关于这个问题的细节。当然,一个纯粹的理论问题也可以!事实上,这是一个很好的问题。我很困惑(尽管我认为静态调度还不可能:/)@LukasKalbertodt这是一个实际问题的简化版本,它指出了关键问题。实际问题是一种地图
或平面地图
。但是映射函数使用一个函数指针,该指针允许它请求额外的数据。映射由此提供数组的项(每个请求仅一个)。如果存在请求的数据,并且所有项都由映射提供,则映射将提供prev迭代器的下一项。您可以将其视为来自SQL的交叉连接。但是计算阵列的成本很高,而且很少需要。这就是为什么它是由函数指针来完成的。很好的答案!但我认为在这种情况下,动态调度是最好的解决方案。这并不是一种性能损失,值得编写卷积代码来避免它。
F: Sized + FnMut(Fn() -> S) -> ()
fn count_calls<S, F, G>(s: S, f: F) -> u32
where
S: Clone,
F: Sized + FnMut(G),
G: Fn() -> S,
struct CallCounter<S> {
counter: u32,
s: S,
}
impl<S: Clone> FnOnce<()> for CallCounter<S> {
type Output = S;
extern "rust-call" fn call_once(self, _: ()) -> Self::Output {
// No point in incrementing the counter here
self.s
}
}
impl<S: Clone> FnMut<()> for CallCounter<S> {
extern "rust-call" fn call_mut(&mut self, _: ()) -> Self::Output {
self.counter += 1;
self.s.clone()
}
}
fn count_calls<S, F>(s: S, mut f: F) -> u32
where
S: Clone,
F: Sized + FnMut(&mut CallCounter<S>), // <----
{
let mut counter = CallCounter {
counter: 0,
s,
};
f(&mut counter); // <-------
counter.counter
}