135 lines
3.7 KiB
Markdown
135 lines
3.7 KiB
Markdown
|
|
### Error Handling in Dynamic Functions
|
|
|
|
When working with the dynamic function signature, error handling is slightly different:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
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:
|
|
|
|
```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", ())?;
|
|
```
|