I was answering someone else's question about trying to name the type of, and pass around, a generic function, and I tried writing this code which seems like it's in the spirit of Rust's types and traits:
use std::fmt::Display;
fn generic_fn<A: Display>(x: A) -> String { format!("→{}←", x) }
fn use_twice<F>(f: F) -> String
where
F: Fn(i32) -> String,
F: Fn(f32) -> String,
{
f(1) + &f(2.0)
}
fn main() {
dbg!(use_twice(generic_fn));
}
However, it fails to compile (on stable Rust 1.52.0):
error[E0631]: type mismatch in function arguments
--> src/main.rs:13:15
|
1 | fn generic_fn<A>(x: A) -> A { x }
| --------------------------- found signature of `fn(i32) -> _`
2 |
3 | fn use_twice<F>(f: F)
| --------- required by a bound in this
...
6 | F: Fn(f32) -> f32,
| -------------- required by this bound in `use_twice`
...
13 | use_twice(generic_fn);
| ^^^^^^^^^^ expected signature of `fn(f32) -> _`
I understand that this means the compiler is requiring the fn item generic_fn to be coerced to a function pointer (the partially specified type fn(f32) -> _). But why? I've heard that fn items have unique zero-sized types — why can't use_twice's parameter f accept that type? Would it cause some trouble in compiling a different program (e.g. type inference failures) if Rust accepted this code? Is this just something that hasn't been implemented yet?
I know it's not a logical impossibility, because if I write my own trait instead of using Fn, then I can explicitly define a generic function that can be passed as a value:
trait PolyFn1<A> {
type Output;
fn apply(&self, x: A) -> Self::Output;
}
use std::fmt::Display;
struct GenericFn;
impl<A: Display> PolyFn1<A> for GenericFn {
type Output = String;
fn apply(&self, x: A) -> String { format!("→{}←", x) }
}
fn use_twice<F>(f: F) -> String
where
F: PolyFn1<i32, Output=String>,
F: PolyFn1<f32, Output=String>,
{
f.apply(1) + &f.apply(2.0)
}
fn main() {
dbg!(use_twice(GenericFn));
}
[src/main.rs:22] use_twice(GenericFn) = "→1←→2←"
Using unstable Rust features, I can even implement Fn this way:
#![feature(fn_traits)]
#![feature(unboxed_closures)]
use std::fmt::Display;
struct GenericFn;
impl<A: Display> FnOnce<(A,)> for GenericFn {
type Output = String;
extern "rust-call" fn call_once(self, args: (A,)) -> String {
self.call(args)
}
}
impl<A: Display> FnMut<(A,)> for GenericFn {
extern "rust-call" fn call_mut(&mut self, args: (A,)) -> String {
self.call(args)
}
}
impl<A: Display> Fn<(A,)> for GenericFn {
extern "rust-call" fn call(&self, args: (A,)) -> String {
format!("→{}←", args.0)
}
}
fn use_twice<F>(f: F) -> String
where
F: Fn(i32) -> String,
F: Fn(f32) -> String,
{
f(1) + &f(2.0)
}
fn main() {
dbg!(use_twice(GenericFn));
}
Given this, I can restate my question as: why don't Rust's normal function items work like this? Why do they have this seemingly avoidable restriction?