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", ())?;
 | |
| ```
 |