Call a Function Within the Caller’s Scope

Peeking Out of The Pure Box

Only scripts

This is only meaningful for scripted functions.

Native Rust functions can never access any Scope.

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

The caller’s scope can be mutated

Changes to variables in the calling Scope persist.

With this syntax, it is possible for a Rhai function to mutate its calling environment.

New variables are not retained

Variables or constants defined within the function are not retained. They remain local to the function.

Although the syntax resembles a Rust macro invocation, it is still a function call.

Caveat emptor

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.