How to pick out all variables captured by a closure?

I'm writing a process macro that needs to pick out all variables captured by a closure expression (allow few lost) like Vue does.
For example, the following closure captured a, and I want to pick its name out as String.

let a = 10;
let closure = pick_out_variables!(|| a * 2);

How can I achieve this, or which crate to use? Thanks a lot.

This screams XY problem. What are you trying to achieve?

And what does “like Vue does” refer to? I’m not supposed to start Googling for 3-letter-words, am I?

I think if you google Vue directly, it indeed would tell you the right thing. But that's not the point. like Vue does just easier to understand.
I want to find out what it captures, so that in the runtime (not in macro), when these variables(or state) change, call the closure.
I'm sorry but I'm not good at expressing. :sweat_smile:

#[proc_macro]
fn my_macro(tokens: TokenStream) -> TokenStream {
  // `tokens` is expected to be a closure expression
  // if `tokens` is `|| a * 20`, `captured` is `["a"]`
  ...
  let captured: Vec<String> = pick_out_variables(tokens);
  ...
}

I believe, it’s impossible to precisely determine which variables a closure captures. For example the closure

|| f(x)

could capture either f or x or both or neither. And procedural macros can only look into syntax.

Presumably, you could get some “best effort” analysis, e.g. interpreting an expression f(x) like x is a local variable, and f is a globally defined function. In any case, one would presumably need to parse the Rust code and then do some form of syntactic analysis to figure out which variables are in scope where and which variables must be (or rather – will probably be) captured variables.

It sounds like your ultimate goal is not only to find out the captured variables, but also do something with them. Try to focus on the “do something with them” part first! Write a simpler version of the macro where the caller explicitly lists the list of variables, and try to see whether the rest of the macro is usable as intended in the first place, before trying to solve the non-trivial problem of automatically figuring out the relevant variables.

2 Likes

Thanks for your answer, which is instructive. I haven't written anything yet, everything is just in my head. But before I give up analyzing captured variables, I wonder if it is feasible only to capture those with prefix self., such as self.some_state? Thanks.

I think I know what are you trying to achieve, as I've used Vue.js before.

This is somewhat sensible, but the problem is that if you are calling self.foo(), which in turns uses some field of Self, then you obviously fails to watch those.

And yes, I would make this statement even further. Don't even write a macro, just write expanded code first and see where you can get! And I would expect major friction just from here.

2 Likes

Oh god, I forgot this. Well, it seems impossible to do with macros now. :fearful:
Thank you.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.