我正在试验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)
或baz(5, 5, 5)
,依此类推。
这似乎最好通过宏或其他语法扩展来完成,但我不确定表达它的最佳方式。到目前为止我所拥有的只是对案例的蛮力枚举,但这似乎既乏味(为每个案例重写相同的表达式)又脆弱(它不支持n + 1个参数):< / p>
// 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)) };
}
我遇到的this pattern似乎相似,但它仍然需要单独列举每个案例。
是否有更好的方法可以更灵活地将fn(X, ...)
包装为只需要一个X
的函数?
答案 0 :(得分:0)
Here 是一个函数 expand
,可以简单地使用您的函数名称调用,并返回“扩展”版本。 IE。你可以这样使用它:
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
。据我所知,我必须使 impl
不同(因此 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
的调用来扩展参数计数的范围。