Consider the following code:
#![feature(fn_traits)]
fn test_fn_2_args<F>(f: Box<F>)
where
F: Fn(i32, i64) -> i32
{
<Box<F> as Fn<(i32, i64)>>::call(&f, (1, 2));
}
fn main(){
test_fn_2_args(Box::new(|first, second| {
println!("{first}, {second}");
first
}));
}
This code requires unstable fn_traits
, but this is not clear why. As per fn_traits docs it's designed for implementing closure-like type which I don't do in this example.
All I do is interpret Box<Fn>
as Fn
which is not clear why is allowed as well since Box<Fn>
does not implement Fn
obviously.
Calling a boxed closure, does not require unstable. To call it, you simply use the standard calling syntax (i.e. a call expression) to call it. Boxed or not.
It doesn't matter whether you have Fn()
or Box<dyn Fn()>
you call it in the same way, i.e. f()
.
The reason you can call them in the same way, is because there is a blanket implementation of impl<F> Fn for Box<F> where F: Fn
.
The reason you can use Fn
, but not Fn::call()
is because Fn
is stable, while Fn::call()
is unstable. Which as you've discovered is part of the unstable fn_traits
feature (see issue #29625).
So the issue is not "interpreting Box<Fn>
as Fn
", the issue is you're attempting to call the unstable Fn::call()
.
In short, doing this is stable:
fn f<F: Fn()>(f: F) {
f();
}
fn boxed_f<F: Fn()>(f: Box<F>) {
f();
}
While doing the following is unstable:
fn f<F: Fn()>(f: F) {
Fn::<()>::call(&f, ());
}
fn boxed_f<F: Fn()>(f: Box<F>) {
Fn::<()>::call(&f, ());
}
All in all, just call the closure like any other function:
fn test_fn_2_args<F>(f: Box<F>)
where
F: Fn(i32, i64) -> i32,
{
f(1, 2);
}
You don't even need Box
:
fn test_fn_2_args<F>(f: F)
where
F: Fn(i32, i64) -> i32,
{
f(1, 2);
}
Again, since there is a blanket implementation of impl<F> Fn for Box<F> where F: Fn
. That also means that changing test_fn_2_args()
to not use Box
, still allows you to call it using a boxed closure. So the following compiles perfectly fine.
fn test_fn_2_args<F>(f: F)
where
F: Fn(i32, i64) -> i32,
{
f(1, 2);
}
fn main() {
test_fn_2_args(|first, second| {
println!("{first}, {second}");
first
});
test_fn_2_args(Box::new(|first, second| {
println!("{first}, {second}");
first
}));
}