Reputation: 48
I am trying to implement a Rust function printf
that can accept closures with different argument signatures. The function already has two implementations - printf1
which accepts closures with two i32
arguments, and printf2
which accepts closures with one i32
argument and returns a closure that also accepts an i32
argument.
(I want to have a printf
function that chooses between them depending on the input or something similar)
fn printf1<F>(f: F, a: i32, b: i32)
where
F: FnOnce(i32, i32) -> i32,
{
println!("Result is {result}", result = f(a, b),)
}
fn printf2<F, G>(f: F, a: i32, b: i32)
where
F: FnOnce(i32) -> G,
G: FnOnce(i32) -> i32,
{
println!("Result is {result}", result = f(a)(b),)
}
I tried implementing a trait DynChainCall
to be able to add the trait as a bound to the F
type parameter of printf
. However, I'm not sure how to add support for closures with two i32
arguments as well as closures that return a closure with a signature of FnOnce(i32) -> i32
.
trait DynChainCall {
fn call_print(&self, a: i32, b: i32) -> i32;
}
impl<F> DynChainCall for F
where
F: FnOnce(i32, i32) -> i32,
{
fn call_print(&self, a: i32, b: i32) -> i32 {
self(a, b)
}
}
impl<F, G> DynChainCall for F
where
F: FnOnce(i32) -> G,
G: FnOnce(i32) -> i32,
{
fn call_print(&self, a: i32, b: i32) -> i32 {
self(a)(b)
}
}
fn printf<P: DynChainCall>(f: P, a: i32, b: i32) {
println!("Result is {}", f.call_print(a, b));
}
-> conflicting implementations of trait DynChainCall
An example of two closures/functions that I would like to pass into printf
:
fn function(a: i32, b: i32) -> i32 {
a + b
}
fn func(a: i32) -> impl Fn(i32) -> i32 {
move |b| a + b
}
I want printf
to automatically check if the passed closure f
accepts one or two arguments and call it accordingly . If the closure accepts one argument and returns a closure with a signature/closure type of FnOnce(i32) -> i32
, then it should call f(a)(b)
. If it returns a i32
then it should call f(a, b)
.
printf(function, 25, 10); // This should work! ( function(25, 10) is called )
printf(func, 25, 10); // This should also work! ( func(25)(10) is called )
How can I implement this behavior of printf
?
Also:
I tried to somehow make a weird enum with two variants to try and do a pattern match.
But I stopped trying to implement this because it is super clunky even if it works.
enum Function {
FnOnceVariant(Box<dyn FnOnce(i32, i32) -> i32>),
FnVariant(Box<dyn Fn(i32) -> i32>)
}
Upvotes: 0
Views: 137
Reputation: 48
I tried some stuff but I can't get it to work with a printf
function. I will use a macro for now. (In case someone needs an alternative) This is how I did it:
trait FunctionCall<O> {
fn call_print(self, a: O, b: O) -> O;
}
trait FunctionComposition<O> {
fn call_print(self, a: O, b: O) -> O;
}
impl<F, O> FunctionCall<O> for F
where
F: FnOnce(O, O) -> O,
{
fn call_print(self, a: O, b: O) -> O {
self(a, b)
}
}
impl<F, G, O> FunctionComposition<O> for F
where
F: FnOnce(O) -> G,
G: FnOnce(O) -> O,
{
fn call_print(self, a: O, b: O) -> O {
self(a)(b)
}
}
macro_rules! printf {
($f:expr, $a:expr, $b:expr) => {
println!("Result is {}", ($f).call_print($a, $b));
};
}
fn function(a: i32, b: i32) -> i32 {
a + b
}
fn func(a: i32) -> impl Fn(i32) -> i32 {
move |b| a + b
}
fn main() {
printf!(function, 22, 33);
printf!(func, 33, 44);
}
->
Result is 55
Result is 77
Upvotes: 0