Variable Resolver

By default, Rhai looks up access to variables from the enclosing block scope, working its way outwards until it reaches the top (global) level, then it searches the Scope that is passed into the Engine::eval call.

There is a built-in facility for advanced users to hook into the variable resolution service and to override its default behavior.

To do so, provide a closure to the Engine via Engine::on_var.

let mut engine = Engine::new();

// Register a variable resolver.
engine.on_var(|name, index, context| {
    match name {
        "MYSTIC_NUMBER" => Ok(Some(42_i64.into())),
        // Override a variable - make it not found even if it exists!
        "DO_NOT_USE" => Err(EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE).into()),
        // Silently maps 'chameleon' into 'innocent'.
        "chameleon" => context.scope().get_value("innocent").map(Some).ok_or_else(|| 
            EvalAltResult::ErrorVariableNotFound(name.to_string(), Position::NONE).into()
        ),
        // Return Ok(None) to continue with the normal variable resolution process.
        _ => Ok(None)
    }
});

1. Avoid having to maintain a custom [`Scope`] with all [variables] regardless of need
   (because a script may not use them all).

2. _Short-circuit_ [variable] access, essentially overriding standard behavior.

3. _Lazy-load_ [variables] when they are accessed, not up-front.
   This benefits when the number of [variables] is very large, when they are timing-dependent,
   or when they are expensive to load.

4. Rename system [variables] on a script-by-script basis without having to construct different [`Scope`]'s.

[Variable] values returned by a variable resolver are treated as _[constants]_.

This is to avoid needing a mutable reference to the underlying data provider which may not be possible to obtain.

To change these [variables], better push them into a custom [`Scope`] instead of
using a variable resolver.

It is possible to return a _shared_ value from a variable resolver.

This is one way to implement [Mutable Global State]({{rootUrl}}/patterns/global-mutable-state.md).

Function Signature

The function signature passed to Engine::on_var takes the following form.

Fn(name: &str, index: usize, context: EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>

where:

ParameterTypeDescription
name&strvariable name
indexusizean offset from the bottom of the current Scope that the variable is supposed to reside.
Offsets start from 1, with 1 meaning the last variable in the current Scope. Essentially the correct variable is at position scope.len() - index.
If index is zero, then there is no pre-calculated offset position and a search through the current Scope must be performed.
contextEvalContextmutable reference to the current evaluation context

and EvalContext is a type that encapsulates the current evaluation context.

Return value

The return value is Result<Option<Dynamic>, Box<EvalAltResult>> where:

ValueDescription
Ok(None)normal variable resolution process should continue, i.e. continue searching through the Scope
Ok(Some(value))value (a Dynamic) of the variable, treated as a constant
Err(Box<EvalAltResult>)error that is reflected back to the Engine, normally EvalAltResult::ErrorVariableNotFound to indicate that the variable does not exist, but it can be any EvalAltResult.