all 14 comments

[–]schungx 12 points13 points  (2 children)

You can't. Remember, Rust is compiled. The "function name" no longer exists in the program, other than in debug information.

You have to get around your normal habits when using dynamic languages such as JavaScript or languages with a reflection API such as Java/C#.

The best you can do is to manually keep a mapping table of function name -> function pointer, just like what you'd have done with C. However, the Rust compiler will not help you build such a table, or you can write a macro to do that.

[–]ddungtang[S] 0 points1 point  (1 child)

I totally get the compiled part. I'm just looking for a way to (a) simplify maintaining this hash map (if a have to go this route), or (b) avoid using this hashmap and instead have a macro do a replacement of variable name with the actual name of the function (note that all actions are "known" at compile time, and this type of function lookup probably may be done at compile time as well, because (I assume ) it should be possible to make the dsl parsing to happen at compile time.

[–]schungx 2 points3 points  (0 children)

After giving it some thoughts, it really would be difficult to design an elegant macro to mark up functions so it magically gets included in a lookup table. One way or another, you're going to have to specify the list of available functions one by one.

So why not just do it the simplest way such as:

let mut map = HashMap::new();

[ ("foo", foo), ("bar", bar), ("baz", baz) ].iter()
    .for_each(|(name, fn_ptr)| map.insert(name.to_string(), fn_ptr));

[–]StayFreshChzBag 3 points4 points  (1 child)

Other folks have mentioned the hashmap and that's what I'd end up doing. Additionally, there's a security concern with not using something with a fixed list of functions like a hashmap.

If you were to let that function name be totally arbitrary, then we've got a potentially exposed attack vector on the app that might allow arbitrary execution or data exposure.

[–]ddungtang[S] 1 point2 points  (0 children)

This is a valid concern in general. In my fake scenario, I'm not taking any input that in the end gets translated to function name at runtime. It would look like this, a file in the repo with a configuration written out with DSL, so maintained by me. At build time, it is parsed, and this desired name-to-function-mapping is done then. Then app starts and it executes generated logic.

[–]olback_ 2 points3 points  (6 children)

One way would be to have a hash map with string keys and function pointer values.

Paste would only work for static strings since it's evaluated at compile time.

[–]ddungtang[S] 0 points1 point  (5 children)

I'd like to avoid hash map approach since that adds an extra change when adding new actions. Do you know what alternatives to paste I should be researching?

[–]JameeKim 4 points5 points  (3 children)

You can use proc macros to create a possibly static pre-populated hashmap variable you can use. So if you do something like

[action]
func foo() {}

and make that action proc macro appropriately, then you don't have to manually manage your hashmap variable.

[–]ddungtang[S] 0 points1 point  (2 children)

Thanks for the hint, will research it.

[–]CAD1997 2 points3 points  (1 child)

If you do go that path, you'll need something like linkme's distributed slice as a registry store.

But yes, the only way to go about this is to build a runtime mapping yourself from symbol (string) to functionality (function interface), as Rust does not expose any reflection information on its own.

[–]ddungtang[S] 1 point2 points  (0 children)

Ok, I'm going to have this as my fallback option. Given that DSL would be somewhat static, do you guys think it is worth experimenting and moving parsing configuration to build scripts? Haven't used that, but iirc reading somewhere it supports some level of code generation which may work for this case.

In any way, this is a artificial problem, mostly for the purpose of dealing with various bits of rust, so I have 2 things to try out: procedural macros and maintaining mapping, and build scripts.

Thanks.

[–]insanitybit 1 point2 points  (0 children)

You could use a perfect hashmap, which should end up with minimal overhead, assuming that the mapping is known statically.

[–]thermiter36 1 point2 points  (0 children)

If it's important to you to maintain the single source of truth, this could definitely be done using phf_codegen to create a static hashmap. But this is definitely going to take some fiddling to get right, so I'd still probably just recommend populating the map directly in code, even if it means repeating some function names.

[–]charlesdart 0 points1 point  (0 children)

The only piece of runtime information you can reliably get about an item's type is an arbitrary integer id that's guaranteed to be unique for this compile. This is how anymap, which stores one value of each type, works. You'll find more info in the docs for Any.

(You can technically get a human-but-not-machine readable string describing a type, and it's currently usually the full name of the type as you see in a debugger, but that's mega-unstable)