Call a Function Within the Caller’s Scope
Peeking Out of The Pure Box
Rhai functions are pure, meaning that they depend on on their arguments and have no access to the calling environment.
When a function accesses a variable that is not defined within that function’s Scope
,
it raises an evaluation error.
It is possible, through a special syntax, to actually run the function call within the Scope
of the parent caller – i.e. the Scope
that makes the function call – and
access/mutate variables defined there.
fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined
x += y; // 'x' is modified in this function
let z = 0; // 'z' is defined in this function's scope
x
}
let x = 1; // 'x' is defined here in the parent scope
foo(41); // error: variable 'x' not found
// Calling a function with a '!' causes it to run within the caller's scope
foo!(41) == 42; // the function can access and mutate the value of 'x'!
x == 42; // 'x' is changed!
z == 0; // <- error: variable 'z' not found
x.method!(); // <- syntax error: not allowed in method-call style
// Also works for function pointers
let f = foo; // <- de-sugars to 'Fn("foo")'
call!(f, 42) == 84; // must use function-call style
x == 84; // 'x' is changed once again
f.call!(41); // <- syntax error: not allowed in method-call style
// But not allowed for module functions
import "hello" as h;
h::greet!(); // <- syntax error: not allowed in namespace-qualified calls
Functions relying on the calling Scope
is often a Very Bad Idea™ because it makes code
almost impossible to reason about and maintain, as their behaviors are volatile and unpredictable.
Rhai functions are normally pure, meaning that you can rely on the fact that they never mutate the outside environment. Using this syntax breaks this guarantee.
Functions called in this manner behave more like macros that are expanded inline than actual function calls, thus the syntax is also similar to Rust’s macro invocations.
This usage should be at the last resort.
YOU HAVE BEEN WARNED.