Is it possible to create make_adapter here? playground link
The basic idea is simple - I’m binding an argument, so I can then create multiple partial applications by calling the result of make_adapter. But I can't figure out how to specify the types.
fn make_adapter<F1, F2>(b: i32) -> impl Fn(F2) -> F1
where F1: Fn(i32) -> (), F2: Fn(i32, i32) -> ()
{
let adapt = |f2: F2| -> F1 {
let f1 = move |a: i32| {
f2(a, b);
};
f1
};
adapt
}
fn main() {
let a: i32 = 123;
let adapt = make_adapter(456);
let f1: Box<dyn Fn(i32) -> ()> = Box::new(
adapt(|a,b| println!("called: {}, {}", a, b))
);
f1(a);
}
Errors:
error[E0308]: mismatched types
--> src/main.rs:9:9
|
2 | fn make_adapter<F1, F2>(b: i32) -> impl Fn(F2) -> F1
| -- expected this type parameter
...
5 | let adapt = |f2: F2| -> F1 {
| -- expected `F1` because of return type
6 | let f1 = move |a: i32| {
| ------------- the found closure
...
9 | f1
| ^^ expected type parameter `F1`, found closure
|
= note: expected type parameter `F1`
found closure `{closure@src/main.rs:6:18: 6:31}`
= help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `F1`
error[E0283]: type annotations needed
--> src/main.rs:17:38
|
16 | let adapt = make_adapter(456);
| ----------------- type must be known at this point
17 | let f1: Box<dyn Fn(i32) -> ()> = Box::new(
| ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
|
You cannot use a type parameterF1 to describe the type of a specific closure that your function returns, because parameters are always notionally chosen by the caller, and your closure type is not equal to what the caller chose. The “correct” signature would be
The caller of make_adapter chooses F1 and F2. Or phrased differently, make_adapter must support every combination of F1 and F2 that meet the stated bounds.
So I guess the exception to this idea of the caller picking type params is the impl Trait return? Is that a kind of hidden type parameter? But as kpreid noted, syntax like → impl Fn(F) → impl Fn(i32) does not work.
It means that there's a distinct output type for every distinct input type F, like how a Vec<u32> and Vec<i32> are different types.
It's sort of like this happens:
trait DefineBoundSecond<F: Fn(i32, i32)> {
type Ty: Fn(i32);
}
struct ImplBoundSecond;
#[define_opaque(BoundSecond)]
fn make_bound_second<F: Fn(i32, i32)>(f2: F, b: i32)
-> <ImplBoundSecond as DefineBoundSecond<F>>::Ty
{ ... }
// Automatically generated due to `define_opaque`
impl<F: Fn(i32, i32)> DefineBoundSecond<F> for ImplBoundSecond {
type Ty = /* ... the unnameable closure type from `make_bound_second` ... */;
}
Every returned closure has to be a different type for different F passed to make_bound_second, because the returned closure captures the f2: F, as if the closure was a struct with a field of type F. The F on the TAIT is what makes it possible for every returned closure to be a different type for different Fs.
If the function is that simple, can you consider using a macro instead? Or is it really a must to have a function? Because having a macro could be easier than dealing with complex generics, and - depending on your code - more efficient