Rust dynamically calls the Rhai function defined by strings
When using the Rhai script engine in Rust, you can dynamically call the Rhai function represented by the incoming string.
Rhai is an embedded scripting language designed for embedding into Rust applications.
This is a basic example
Shows how to call Rhai function passed in with a string in Rust.
First, make sure you have added Rhai to yourIn the file:
[dependencies] rhai = "0.19" # Please check the latest version number
You can then use the following code to call the Rhai function passed in with the string:
use rhai::{Engine, EvalAltResult, FnPtr, Module, Scope}; fn main() -> Result<(), Box<dyn std::error::Error>> { // Create an Rhai engine instance let mut engine = Engine::new(); // Define a Rhai module with some functions let mut module = Module::new(); module.insert_fn("greet", |name: String| format!("Hello, {}", name)); module.insert_fn("add", |a: i32, b: i32| a + b); // Register the module into the engine engine.register_module(module)?; // Create a scope let mut scope = Scope::new(); // Example: The function name and its parameters to be called let function_name = "greet".to_string(); let args: Vec<Box<dyn FnPtr>> = vec![Box::new(|_| "World".to_string()) as Box<dyn FnPtr>]; // Call function let result: EvalAltResult = engine.eval_expression_with_scope( &format!("({})", function_name), &mut scope, ().cloned().collect::<Vec<_>>(), )?; // Print the result match result { EvalAltResult::Value(value) => println!("Result: {}", ()?), _ => println!("Result is not a value"), } Ok(()) }
However, the above code has some limitations and simplifications:
-
Parameter pass: In the above example, parameter passing is by creating a
FnPtr
and pass toeval_expression_with_scope
Achieved. However, this method is quite cumbersome and only works for simple function signatures. - Function name processing: Function names are embedded directly into expressions via string formatting, which means you need to make sure that the incoming function names are safe (i.e., it won't cause Rhai to execute unsafe code).
A more robust approach is to use Rhai'sFnCall
function, but this requires more setup and error handling.
This is a more general approach, but slightly more complicated
use rhai::{Engine, EvalAltResult, Module, Scope}; use rhai::serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] struct CallArgs { func: String, args: Vec<String>, } fn main() -> Result<(), Box<dyn std::error::Error>> { // Create an Rhai engine instance let mut engine = Engine::new(); // Define a Rhai module with some functions let mut module = Module::new(); module.insert_fn("greet", |name: String| format!("Hello, {}", name)); module.insert_fn("add", |a: i32, b: i32| a + b); // Register the module into the engine engine.register_module(module)?; // Create a scope let mut scope = Scope::new(); // Example: The function name and its parameters to be called let call_args = CallArgs { func: "greet".to_string(), args: vec!["Alice".to_string()], }; // Convert the parameter to Rhai value let rhai_args: rhai::Array = call_args.args.into_iter().map(|arg| rhai::Value::from(arg)).collect(); // Define a temporary Rhai function to call the target function let call_code = format!( r#" fn call_func(func_name: String, args: Array) -> Any {{ let func = match func_name.as_str() {{ "greet" => greet, "add" => add as fn(i32, i32) -> i32, _ => return "Function not found".into(), }}; match (func, ()) {{ (greet, 1) => greet(args[0].cast::<String>()?), (add, 2) => add(args[0].cast::<i32>()?, args[1].cast::<i32>()?), _ => return "Invalid argument count".into(), }} }} call_func("{}", {}) "#, call_args.func, rhai_args ); // Call function let result: EvalAltResult = engine.eval_expression(&call_code, &mut scope)?; // Print the result match result { EvalAltResult::Value(value) => println!("Result: {}", ()?), _ => println!("Result is not a value"), } Ok(()) }
In this more general example, we define aCallArgs
The structure stores the function name and parameters, and then builds a temporary Rhai script that calls the corresponding Rhai function based on the function name and number of parameters.
This approach provides greater flexibility, but is also more complex and requires handling more error situations.
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.