带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
}