97 lines
5.9 KiB
Markdown
97 lines
5.9 KiB
Markdown
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<Option<Dynamic>, Box<EvalAltResult>>
|
|
> ```
|
|
|
|
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.<br/>Offsets start from 1, with 1 meaning the last [variable] in the current [`Scope`]. Essentially the correct [variable] is at position `scope.len() - index`.<br/>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<Option<Dynamic>, Box<EvalAltResult>>` 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<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`. |
|