Just curious, if there is any runtime performance difference between the implementations as following;
and is there any general guideline for choosing between these two patterns? thanks;
With code samples, it's generally useful to only post ones that you have shown to a compiler, so there aren't any errors you aren't aware of. If you're asking performance questions in particular, make sure your code compiles and runs correctly, doing something useful.
Otherwise, the answer might really only be able to focus on those overlooked errors in the code, as is the case here:
In this question, you're comparing alternatives that don't exist. You cannot use a Fn() -> () parameter without having a value of it, so your second code can't do anything useful, and PhantomData<F> is relatively useless, too. You need an actual value func: F in order to be able to call it.
It's not impossible that there was a reasonable question you've wanted to ask, about actual code patterns that actually work and might seem different (in which case, please follow up and ask your question with more proper code samples); but these code examples right now don't really give me sufficient information to make a guess (and it'd only be a guess anyways) with much confidence as to what you were after. Or perhaps the alternatives you imagined weren't viable alternatives at all.
I see you've updated the question. The two versions of your code are identical in performance. Using func: impl Fn() parameters sort-of "desugars" to usage of a generic anyways, so it's doing the same basic kind of thing in that regard. The only slight difference is that calling of an Fn also passes a &self param (compare the call method in Fn with your Get5 trair's get_5 method), however for captureless closures or function items like get_5, this &self parameter is unused and will be optimized away, anyways.
As also shown here: Compiler Explorer (godbolt.org)
I was wondering if there is situation that is complicated enough so that one pattern outshines another, but I guess the compiler will be able to optimize them most cases;
Only as soon as you go to fn() function pointers or Box<dyn Fn…> trait objects is when you start getting overhead from the abstraction of 'passing functions to functions' in Rust. That's when[1] dynamic function calls get introduced which would dereference and call a function pointer at run time, and more importantly are harder to inline[2] (though even this overhead is generally only a concern if you're in a hot loop).
For your code example it's also of note that a function item like get_5 or Get5Struct::get_5 does inherently not have the type fn() -> u8 (i. e. it's not a function pointer), but instead each function item has its own anonymous type[3]; though it can be coerced into such a function pointer easily.
at least before potential optimizations that eliminate unnecessary virtual function calls when the concrete value is known / apparent e.g. after I lining ↩︎
whereas closures in particular are super easy to inline often each closure expression (which has its own anynymous type) is only used in one place anyways, and inlining the sole use of a function is basically always a win, so it'll happen very reliably ↩︎
of zero size, so the thing you're actually passing doesn't even exist at all at run time ↩︎
thanks, that maeks a lot of sense; and pattern using function parameter gives options to passing in functions deternmined in runtime (although with some overhead since the upstream most likely have dynamic dispatch involves in), it's also less verbose to write out;
I guess this is one of the reason that the SerDe deserialize trait chooses to pass in visitor as function parameter;