Variable Resolver ================= {{#include ../links.md}} [`Engine::on_var`]: https://docs.rs/rhai/{{version}}/rhai/struct.Engine.html#method.on_var 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`]. ```rust 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) } }); ``` ```admonish info.small "Benefits of using a variable resolver" 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. ``` ```admonish warning.small "Returned values are constants" [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. ``` ```admonish tip.small "Tip: Returning shared values" 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. > ```rust > Fn(name: &str, index: usize, context: EvalContext) -> Result, Box> > ``` where: | Parameter | Type | Description | | --------- | :-------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | `&str` | [variable] name | | `index` | `usize` | an 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. | | `context` | [`EvalContext`] | mutable reference to the current _evaluation context_ | and [`EvalContext`] is a type that encapsulates the current _evaluation context_. ### Return value The return value is `Result, Box>` where: | Value | Description | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `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)` | 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`. |