I want to bundle two closures together in a struct. One closure is there to test if the inputs are valid, and the other closure is to actually do the stuff. Here is one variation that works:
struct Foo<T, F>
{
test: T,
func: F,
}
fn make_foo<A, B, T, F>(test: T, func: F) -> Foo<T, F>
where
T: Fn(A) -> bool,
F: Fn(A) -> B
{
Foo {
test,
func,
}
}
fn main() {
let foo = make_foo(|a: i32| {a > 5}, |a: i32| { a + 8 });
if (foo.test)(10) {
println!("{}", (foo.func)(10));
}
else {
println!("Input too small");
}
}
However, Rust being Rust, there are a few issues that seem non-idiomatic. In particular, there is no check to see if test
was actually run, and there is no enforcing that test
and func
are run with the same arguments.
So, I set out to try to fix that. My idea is that foo.test
returns an Option<Fn>
, where the Some
variant contains func
, but with the input I want to use already fed to it. So something like this as a first draft:
struct Foo<T>
{
test: T
}
fn make_foo<A, B, T, F, H>(t: T, f: F) -> Foo<H>
where
T: Fn(A) -> bool,
F: Fn(A) -> B,
H: Fn(A) -> Option<Fn() -> B>,
{
Foo {
test: |a: A| {
if t(a) {
Some(| | {f(a)})
}
else {
None
}
}
}
}
fn main() {
let foo = make_foo(|a: i32| {a > 5}, |a: i32| { a + 8 });
if let Some(f) = (foo.test)(10) {
println!("{}", f());
}
else {
println!("Input too small");
}
}
This complains that Fn() -> B
needs dyn
, which of course also means it has unknown size at compile time. So we Box
it:
fn make_foo<A, B, T, F, H>(t: T, f: F) -> Foo<H>
where
T: Fn(A) -> bool,
F: Fn(A) -> B,
H: Fn(A) -> Option<Box<dyn Fn() -> B>>,
{
Foo {
test: |a: A| {
if t(a) {
Some(Box::new(| | {f(a)}))
}
else {
None
}
}
}
}
Now it complains that it expected type parameter
H, found closure
on the closure that I have written here, and I don't really know what that means. It also complains that it now wants an explicit type signature on foo
inside main
, and given that that type includes closures, I don't think that I can even do that. Also, I haven't even gotten to touch the a
parameter yet. Presumably I need a move
in there.
Have I dug myself into a hole here that I cannot get out of, or is there some decent way to solve this? Or is there a more ergonomic way to solve the two issues I had with my functioning code?