Проблема с макросами заключается в том, что они не имеют никакой информации о типе, и вам необходимо знать, сколько аргументов принимает функция, или какова длина массива (и предположим, что они равны). Поэтому, если вы хотите сделать это исключительно с помощью макросов, вам придется явно указать эту информацию, например, call!(&add, params, 2)
.
Однако вы можете решить эту проблему с помощью признаков, поскольку они do имеют введите информацию. Вы можете создать черту FnExpandArgs
:
// Helper trait for calling a function with a list of arguments.
trait FnExpandArgs<Args> {
// Return type.
type Output;
/// Call function with an argument list.
fn call_expand_args(&self, args: Args) -> Self::Output;
}
Затем вы можете реализовать ее для каждого необходимого вам количества аргументов. К сожалению, каждое число нуждается в собственной реализации, поэтому оно будет немного многословным.
// Example implementation for 2 arguments
impl<F, T, R> FnExpandArgs<[T; 2]> for F
where
F: Fn(T, T) -> R
{
type Output = R;
fn call_expand_args(&self, args: [T; 2]) -> R {
// Expand array of arguments
let [arg0, arg1] = args;
// Call function
self(arg0, arg1)
}
}
И, если у вас есть черта в области видимости, теперь вы можете вызывать его для любой функции, которая принимает идентичные аргументы:
fn add(x:u32, y:u32) -> u32 { x + y }
let params: [u32; 2] = [2 ,3];
assert_eq!(add.call_expand_args(params), 5);
Вы также можете реализовать макрос, который вы хотели, основываясь на этом, если вы действительно этого хотите, хотя я не уверен, что он добавляет гораздо большую ценность:
marco_rules! call {
($function:expr, $params:expr) => {
FnExpandArgs::call_expand_args(&$function, $params)
}
}
fn add(x:u32, y:u32) -> u32 { x + y }
let params: [u32; 2] = [2 ,3];
assert_eq!(call!(add, params), 5);
Еще одно преимущество Это означает, что вы также можете реализовать его для других типов, кроме массивов, таких как кортежи:
impl<F, T0, T1, R> FnExpandArgs<(T0, T1)> for F
where
F: Fn(T0, T1) -> R
{
type Output = R;
fn call_expand_args(&self, args: (T0, T1)) -> R {
let (arg0, arg1) = args;
self(arg0, arg1)
}
}
fn shift(x:u32, y:u8) -> u32 { x << y }
let params: (u32, u8) = (2, 3);
assert_eq!(shift.call_expand_args(params), 16);
Пример игровой площадки