Yes and no. async functions are really just functions that happen to return a type that implements Future, so you can write something like this:
fn my_func<F,R>(f: &F)->R where f:Fn()->R {
// do stuff...
f()
}
This is often pretty limiting, though, because you can't really do anything with the return type other than return it from your own function. Without more information about what you're trying to do, it's hard to say whether or not it's possible.
Two function pointers, one returning T and the other, async version returning some type F where F implements Future<Output=T> are distinct types. So you can't pass either one as your argument without a wrapper. That could be an enum for example, or you could convert the synchronous version into an asynchronous version by wrapping it in a closure with an async body:
Just a note that every example in this thread has used a type parameter for the return value, which won't work for a Fn(...) that returns more than one type (e.g. where the return type captures a lifetime from the inputs).
(I think the OP has their answer, so I'm not going to go through a full reproduction of a helper trait for that.)
Even if it's technically possible, I advise not to do it. It will create a needlessly complex API.
Pick one, and offer just that.
If you only support async fn, then users who have sync code to call will be easily able to adapt by wrapping it in async { regular_fn() } block.
Even if you only support sync fn, then move || handle.block_on(async {…}) might be possible for callers.
So you should decide whether you call the callbacks in context that needs to support async, or whether the callback can block you code. Don't add vague complex interfaces "just in case".