struct Callback<T>(fn() -> T);
impl<T> Callback<T> {
pub fn new<F>(callback: F) -> Self
where
F: Into<fn() -> T>,
{
Self(callback.into())
}
pub fn call(&self) -> T {
self.0()
}
}
fn hello_world() {
println!("Hello World!");
}
fn main() {
let callback = Callback::new(hello_world);
// This work fine!
//let callback = Callback::new(hello_world as fn() -> ());
callback.call();
}
/Users/asinotov/.cargo/bin/cargo build --color=always --message-format=json-diagnostic-rendered-ansi
Compiling playground3 v0.1.0 (/Users/asinotov/CLionProjects/playground3)
error[E0277]: the trait bound `fn() -> _: From<fn() {hello_world}>` is not satisfied
--> src/main.rs:21:34
|
21 | let callback = Callback::new(hello_world);
| ------------- ^^^^^^^^^^^ the trait `From<fn() {hello_world}>` is not implemented for `fn() -> _`
| |
| required by a bound introduced by this call
|
= note: required because of the requirements on the impl of `Into<fn() -> _>` for `fn() {hello_world}`
note: required by a bound in `Callback::<T>::new`
--> src/main.rs:6:12
|
4 | pub fn new<F>(callback: F) -> Self
| --- required by a bound in this
5 | where
6 | F: Into<fn() -> T>,
| ^^^^^^^^^^^^^^^ required by this bound in `Callback::<T>::new`
error: aborting due to previous error
I think ( and I am very far from being an expert ) that when working with function pointers type inference often doesn't work without some extra type hints, and this is probably the problem here.
Why do you have the constructor F: Into<fn() -> T>-based? What's your use-case where directly passing the fn() -> T is too inconvenient? Removing this Into step will make this particular use-case more convenient (i. e. it will probably just work; though I haven't tested it yet).
Note that AFAICT the compiler is not complaining about the difference between fn() and fn() -> () (there is no difference between those) but the difference between a function pointer fn() and the function type of hello_world[1] (a zero-sized type that can be coerced into a function pointer but is not a function pointer, and thus doesn't implement Into<fn()>; yet this way of making the fn new generic precludes implicit coercion at this point.)
which the compiler error message writes as âfn() {hello_world}â âŠī¸
The closest thing I can think of is #95132, but I agree with @steffahn -- this is about the distinction between impl Into and the ability to coerce. The latter does not imply the former.
Ah, well then you could consider just always using Box<dyn Fn -> T>. If the typical use-case for function pointers would have been to store some non-capturing closures or function items, note that boxing those does not even involve any allocation (since they're zero-sized) and the overhead when called, compared to function pointers, is minimal.[1]
Only if you already have some function pointers then turning those into Box<dyn Fn> would need to allocate, so unless that's an important use-case for you...
If you don't plan on regularly paying in pre-boxed functions, you could also make your constructor take a F: impl Fn() -> T and call Box::new inside it. The downside is that passing an already boxed Box<dyn Fn() -> T> would then result in double-boxing; even if this is a problem for you, there'd then still be the option to just offer two constructors; one for convenience taking F: impl Fn() -> T and another constructor taking Box<dyn Fn() -> T> while avoiding the redundant extra boxing.
There's some overhead in the first place only because looking up the function pointer in the vtable is cheap, but it is an additional step. âŠī¸