Rust's macros read in some source text and generate some more source code, so anything a human can do with an editor, a proc-macro can do automatically.
That means your #[func_log_macro] custom attribute will need to generate a function that stashes its arguments "somewhere" before passing them to the wrapped test() function. You'd also need to provide a way for your FuncLog to get at the values.
Regardless of the approach, you are probably going to run into a couple problems that you normally wouldn't see in a garbage collected language like JavaScript, Java, or Python.
Moves - in general, if you stash away your function's arguments, you can't also pass them to test() by value because they would have been moved. This could be "fixed" by making a copy of the value with clone(), but that has non-trivial performance implications.
Generics - if I don't know the concrete type for b: impl number, then how can I store it in a static variable? You'll see I used ... in the above snippet because I didn't know what to write. If test() is first called where b is a f32, then the next call passes in b: u64, the tuple (i32, f32) and (i32, u64) (our arguments) can't be put in the same Vec because a Vec requires each of its elements to have the exact same type because of how things are laid out in memory
Ownership & synchronisation - Does the FuncLog own any values that were captured during any test() calls between the time it was created and the time it goes out of scope? What if I create multiple FuncLog objects in my main() function that are alive at the same time? And how does all of this fit into threading?
Thanks for reply. generics is a complex problem, just ignore it. i just wrote a macro, but don't know how to got the input/output.
pub(crate) fn running_info(_attr: TokenStream, func: TokenStream) -> TokenStream {
let func = parse_macro_input!(func as ItemFn);
let func_vis = &func.vis; // like pub
let func_block = &func.block; // { some statement or expression here }
let func_decl = func.sig;
let func_name = &func_decl.ident; // function name
let func_name_string = func_name.to_string();
let func_generics = &func_decl.generics;
let func_inputs = &func_decl.inputs;
let func_output = &func_decl.output;
let caller = quote! {
#func_vis fn #func_name #func_generics(#func_inputs) #func_output {
use std::time;
let start = time::Instant::now();
#func_block
println!("{}({}) -> {}: {:?}", #func_name_string, ????args, ????result, start.elapsed());
}
};
caller.into()
}