2

我正在尝试使用 Rust 中的不同模式,并想尝试动​​态调用具有相似签名但参数数量不同的几个函数之一。例如:

fn foo(x: i32) -> i32 { x }
fn bar(x1: i32, x2: i32) -> i32 { x1 + x2 }
fn baz(x1: i32, x2: i32, x3: i32) -> i32 { x1 + x2 + x3 }

我希望能够将所有参数的值传递给这些方法,以便它们可以互换调用 - 所以如果输入是5我希望能够调用foo(5), bar(5, 5), orbaz(5, 5, 5)等​​等。

似乎这最好通过宏或其他语法扩展来完成,但我不确定表达它的最佳方式。到目前为止,我所拥有的只是对案例的强力枚举,但这似乎既乏味(为每个案例重新编写相同的表达式)又脆弱(它不支持 n+1 个参数):

// Takes a function and an expected number of args and returns a closure that
// takes exactly one arg and passes it n times to the given function.
macro_rules! expand {
    ($func:ident, 0) => { Box::new(|_n: i32| $func()) };
    ($func:ident, 1) => { Box::new(|n: i32| $func(n)) };
    ($func:ident, 2) => { Box::new(|n: i32| $func(n, n)) };
    ($func:ident, 3) => { Box::new(|n: i32| $func(n, n, n)) };
}

我遇到了这种看起来很相似的模式,但它仍然需要分别列举每个案例。

有没有更好的方法可以更灵活地将 a 包装fn(X, ...)为一个恰好采用 one 的函数X

游乐场示例

4

1 回答 1

0

是一个expand可以简单地使用您的函数名称调用的函数,并返回“扩展”版本。即你可以这样使用它:

fn main() {
    let state = 15;
    let vs: Vec<Box<dyn Fn(i32) -> i32>> = vec![
        expand(foo),
        expand(bar),
        expand(baz),
        expand(|x| x+1), // with lambdas
        expand(move |x| x+state), // with stateful lambdas
    ];
    for f in &vs {
        println!("{:?}", f(1));
    }
}

fn foo(x: i32) -> i32 { x }
fn bar(x1: i32, x2: i32) -> i32 { x1 + x2 }
fn baz(x1: i32, x2: i32, x3: i32) -> i32 { x1 + x2 + x3 }

它通过定义TExpand为函数类型实现的特征来工作。据我所知,我必须使impls 不同(因此TplDummy参数)。

trait TExpand<T, R, TplDummy> {
    fn call(&self, t: T) -> R;
}
macro_rules! ignore_ident{
    ($id:ident, $($t:tt)*) => {$($t)*};
}
macro_rules! impl_expand{
    () => {};
    ($t0:ident $($t:ident)*) => {
        impl_expand!($($t)*);
        impl<T: Copy, R, F:Fn($(ignore_ident!($t, T),)*)->R> TExpand<T, R, ($(ignore_ident!($t, T),)*)> for F {
            #[allow(unused_variables)] // for nullary function
            fn call(&self, t: T) -> R {
                (self)($(ignore_ident!($t, t),)*)
            }
        }
    }
}
impl_expand!(t0 t1 t2 t3);

fn expand<TplDummy, T, R, F:TExpand<T, R, TplDummy>+'static>(f: F) -> Box<dyn Fn(T)->R> {
    Box::new(move |t| f.call(t))
}

您可以通过调整对 的调用来扩展参数计数的范围impl_expand

于 2021-01-15T15:22:53.843 回答