3.7 KiB
3.7 KiB
Error Handling in Dynamic Functions
When working with the dynamic function signature, error handling is slightly different:
fn dynamic_function(ctx: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
// Get the position information from the context
let pos = ctx.position();
// Validate arguments
if args.len() < 2 {
return Err(Box::new(EvalAltResult::ErrorRuntime(
format!("Expected at least 2 arguments, got {}", args.len()),
pos
)));
}
// Try to convert arguments with proper error handling
let arg1 = match args[0].as_int() {
Ok(val) => val,
Err(_) => return Err(Box::new(EvalAltResult::ErrorMismatchOutputType(
"Expected first argument to be an integer".into(),
pos,
"i64".into()
)))
};
// Process with error handling
if arg1 <= 0 {
return Err(Box::new(EvalAltResult::ErrorRuntime(
"First argument must be positive".into(),
pos
)));
}
// Return success
Ok(Dynamic::from(arg1 * 2))
}
Advanced Patterns
Working with Function Pointers
You can create function pointers that bind to Rust functions:
fn my_awesome_fn(ctx: NativeCallContext, args: &mut[&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
// Check number of arguments
if args.len() != 2 {
return Err("one argument is required, plus the object".into());
}
// Get call arguments
let x = args[1].try_cast::<i64>().map_err(|_| "argument must be an integer".into())?;
// Get mutable reference to the object map, which is passed as the first argument
let map = &mut *args[0].as_map_mut().map_err(|_| "object must be a map".into())?;
// Do something awesome here ...
let result = x * 2;
Ok(result.into())
}
// Register a function to create a pre-defined object
engine.register_fn("create_awesome_object", || {
// Use an object map as base
let mut map = Map::new();
// Create a function pointer that binds to 'my_awesome_fn'
let fp = FnPtr::from_fn("awesome", my_awesome_fn)?;
// ^ name of method
// ^ native function
// Store the function pointer in the object map
map.insert("awesome".into(), fp.into());
Ok(Dynamic::from_map(map))
});
Creating Rust Closures from Rhai Functions
You can encapsulate a Rhai script as a Rust closure:
use rhai::{Engine, Func};
let engine = Engine::new();
let script = "fn calc(x, y) { x + y.len < 42 }";
// Create a Rust closure from a Rhai function
let func = Func::<(i64, &str), bool>::create_from_script(
engine, // the 'Engine' is consumed into the closure
script, // the script
"calc" // the entry-point function name
)?;
// Call the closure
let result = func(123, "hello")?;
// Pass it as a callback to another function
schedule_callback(func);
Calling Rhai Functions from Rust
You can call Rhai functions from Rust:
// Compile the script to AST
let ast = engine.compile(script)?;
// Create a custom 'Scope'
let mut scope = Scope::new();
// Add variables to the scope
scope.push("my_var", 42_i64);
scope.push("my_string", "hello, world!");
scope.push_constant("MY_CONST", true);
// Call a function defined in the script
let result = engine.call_fn::<i64>(&mut scope, &ast, "hello", ("abc", 123_i64))?;
// For a function with one parameter, use a tuple with a trailing comma
let result = engine.call_fn::<i64>(&mut scope, &ast, "hello", (123_i64,))?;
// For a function with no parameters
let result = engine.call_fn::<i64>(&mut scope, &ast, "hello", ())?;