diff --git a/README.md b/README.md index a7b129f0..50792756 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,16 @@ let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?; let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?; ``` +For more control, construct all arguments as `Dynamic` values and use `Engine::call_fn_dynamic`: + +```rust +let result: Dynamic = engine.call_fn_dynamic(&mut scope, &ast, "hello", + &mut [ String::from("abc").into(), 123_i64.into() ])?; +``` + +However, beware that `Engine::call_fn_dynamic` _consumes_ its arguments, meaning that all arguments passed to it +will be replaced by `()` afterwards. To re-use the arguments, clone them beforehand and pass in the clone. + ### Creating Rust anonymous functions from Rhai script [`Func`]: #creating-rust-anonymous-functions-from-rhai-script diff --git a/RELEASES.md b/RELEASES.md index 286aac4a..d0a50a9a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -41,6 +41,7 @@ New features * Set limit on maximum level of nesting expressions and statements to avoid panics during parsing. * New `EvalPackage` to disable `eval`. * `Module::set_getter_fn`, `Module::set_setter_fn` and `Module:set_indexer_fn` to register getter/setter/indexer functions. +* `Engine::call_fn_dynamic` for more control in calling script functions. Speed enhancements ------------------ @@ -60,6 +61,8 @@ Speed enhancements excessive cloning. For example, if `a` is a large array, getting its length in this manner: `len(a)` used to result in a full clone of `a` before taking the length and throwing the copy away. Now, `a` is simply passed by reference, avoiding the cloning altogether. +* A custom hasher simply passes through `u64` keys without hashing to avoid function call hash keys + (which as by themselves `u64`) being hashed twice. Version 0.14.1 diff --git a/src/api.rs b/src/api.rs index 8a30a480..1d593023 100644 --- a/src/api.rs +++ b/src/api.rs @@ -997,6 +997,7 @@ impl Engine { } /// Call a script function defined in an `AST` with multiple arguments. + /// Arguments are passed as a tuple. /// /// # Example /// @@ -1040,6 +1041,67 @@ impl Engine { args: A, ) -> Result> { let mut arg_values = args.into_vec(); + let result = self.call_fn_dynamic(scope, ast, name, arg_values.as_mut())?; + + let return_type = self.map_type_name(result.type_name()); + + return result.try_cast().ok_or_else(|| { + Box::new(EvalAltResult::ErrorMismatchOutputType( + return_type.into(), + Position::none(), + )) + }); + } + + /// Call a script function defined in an `AST` with multiple `Dynamic` arguments. + /// + /// ## WARNING + /// + /// All the arguments are _consumed_, meaning that they're replaced by `()`. + /// This is to avoid unnecessarily cloning the arguments. + /// Do you use the arguments after this call. If you need them afterwards, + /// clone them _before_ calling this function. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box> { + /// # #[cfg(not(feature = "no_function"))] + /// # { + /// use rhai::{Engine, Scope}; + /// + /// let engine = Engine::new(); + /// + /// let ast = engine.compile(r" + /// fn add(x, y) { len(x) + y + foo } + /// fn add1(x) { len(x) + 1 + foo } + /// fn bar() { foo/2 } + /// ")?; + /// + /// let mut scope = Scope::new(); + /// scope.push("foo", 42_i64); + /// + /// // Call the script-defined function + /// let result = engine.call_fn_dynamic(&mut scope, &ast, "add", &mut [ String::from("abc").into(), 123_i64.into() ])?; + /// assert_eq!(result.cast::(), 168); + /// + /// let result = engine.call_fn_dynamic(&mut scope, &ast, "add1", &mut [ String::from("abc").into() ])?; + /// assert_eq!(result.cast::(), 46); + /// + /// let result= engine.call_fn_dynamic(&mut scope, &ast, "bar", &mut [])?; + /// assert_eq!(result.cast::(), 21); + /// # } + /// # Ok(()) + /// # } + /// ``` + #[cfg(not(feature = "no_function"))] + pub fn call_fn_dynamic( + &self, + scope: &mut Scope, + ast: &AST, + name: &str, + arg_values: &mut [Dynamic], + ) -> Result> { let mut args: StaticVec<_> = arg_values.iter_mut().collect(); let lib = ast.lib(); let pos = Position::none(); @@ -1051,16 +1113,7 @@ impl Engine { let mut state = State::new(); let args = args.as_mut(); - let result = self.call_script_fn(scope, &mut state, &lib, name, fn_def, args, pos, 0)?; - - let return_type = self.map_type_name(result.type_name()); - - return result.try_cast().ok_or_else(|| { - Box::new(EvalAltResult::ErrorMismatchOutputType( - return_type.into(), - pos, - )) - }); + self.call_script_fn(scope, &mut state, &lib, name, fn_def, args, pos, 0) } /// Optimize the `AST` with constants defined in an external Scope.