这可以在稳定的Rust中使用通用函数或特质来完成。下面是一个示例,它使用特质将2元组传递给接受两个参数的函数。
fn main() {
let tuple = (0, "hello");
takes2.call(tuple);
}
fn takes2(a: u8, b: &str) {}
trait Call2<A, B, Z> {
fn call(self, args: (A, B)) -> Z;
}
impl<F, A, B, Z> Call2<A, B, Z> for F
where
F: FnOnce(A, B) -> Z,
{
fn call(self, (a, b): (A, B)) -> Z {
self(a, b)
}
}
这在大多数情况下可能有些过度,但如果你需要在很多地方做这种事情,使用这个通用代码可能是值得的。
相同的思路可以扩展到任意大小的元组。如果你真的想要疯狂一点,甚至可以使用宏来定义类似的特性,适用于任意数量的参数。
extern crate paste;
fn main() {
let pair = (0, "hello");
let triple = (0, "hello", 1024);
let quad = (0, "hello", 1024, 3.14);
takes2.call(pair);
takes3.call(triple);
takes4.call(quad);
}
fn takes2(a: u8, b: &str) {}
fn takes3(a: u8, b: &str, c: usize) {}
fn takes4(a: u8, b: &str, c: usize, d: f64) {}
define_tuple_calls!(A, B, C, D);
macro_rules! define_tuple_calls {
() => {};
($A:ident $(, $T:ident)* $(,)?) => {
paste::paste! {
trait [<Call $A $($T)*>]<$A, $($T,)* Z> {
fn call(self, args: ($A, $($T,)*)) -> Z;
}
impl<F, $A, $($T,)* Z> [<Call $A $($T)*>]<$A, $($T,)* Z> for F
where
F: FnOnce($A, $($T,)*) -> Z,
{
#[allow(non_snake_case)]
fn call(self, ($A, $($T,)*): ($A, $($T,)*)) -> Z {
self($A, $($T,)*)
}
}
}
define_tuple_calls!($($T,)*);
};
}
use define_tuple_calls;
如果你想传递一组元组和独立的参数,就像
take4.call((0, "hello"), 1024, 3.14)
这样,情况会变得相当复杂。我可以看到一种方法,你可以将输入转换为嵌套的元组
((0, "hello"), 1024, 3.14)
,然后将其作为参数传递给方法,如
take4.call(((0, "hello"), 1024, 3.14))
。调用方法需要更通用。它不再接受
(A, B, C, D)
,而是接受
impl FlattensTo<(A, B, C, D)>
,其中
FlattensTo<T>
是一个类似于
Into<T>
的特质,你需要定义并实现它,以支持任意组合的嵌套元组或值。要全面实现这一点而不产生大量冗余代码,你可能需要编写一个过程宏。