Scope – Maintaining State
By default, Rhai treats each Engine invocation as a fresh one, persisting only the functions
that have been registered but no global state.
This gives each evaluation a clean starting slate.
In order to continue using the same global state from one invocation to the next, such a state
(a Scope) must be manually created and passed in.
All Scope variables and constants have values that are Dynamic, meaning they can store
values of any type.
Under sync, however, only types that are Send + Sync are supported, and the entire Scope
itself will also be Send + Sync. This is extremely useful in multi-threaded applications.
Scope has a lifetime parameter, in the vast majority of cases it can be omitted and
automatically inferred to be 'static.
Currently, that lifetime parameter is not used.  It is there to maintain backwards compatibility
as well as for possible future expansion when references can also be put into the Scope.
The lifetime parameter is not guaranteed to remain unused for future versions.
In order to put a Scope into a struct, use Scope<'static>.
Scope also has a const generic parameter, which is a number that defaults to 8.
It indicates the number of entries that the Scope can keep inline without allocations.
The larger this number, the larger the Scope type gets, but allocations will happen far
less frequently.
A smaller number makes Scope smaller, but allocation costs will be incurred when the
number of entries exceed the inline capacity.
Scope API
| Method | Description | 
|---|---|
| newinstance method | create a new empty Scope | 
| with_capacityinstance method | create a new empty Scopewith a specified initial capacity | 
| len | number of variables/constants currently within the Scope | 
| rewind | rewind (i.e. reset) the Scopeto a particular number of variables/constants | 
| clear | remove all variables/constants from the Scope, making it empty | 
| is_empty | is the Scopeempty? | 
| is_constant | is the particular variable/constant  in the Scopea constant? | 
| push,push_constant | add a new variable/constant into the Scopewith a specified value | 
| push_dynamic,push_constant_dynamic | add a new variable/constant into the Scopewith aDynamicvalue | 
| set_or_push<T> | set the value of the last variable within the Scopeby name if it exists and is not constant; add a new variable into theScopeotherwise | 
| contains | does the particular variable or constant exist in the Scope? | 
| get_value<T> | get the value of the last variable/constant within the Scopeby name | 
| set_value<T> | set the value of the last variable within the Scopeby name, panics if it is constant | 
| remove<T> | remove the last variable/constant from the Scopeby name, returning its value | 
| get | get a reference to the value of the last variable/constant within the Scopeby name | 
| get_mut | get a reference to the value of the last variable within the Scopeby name,Noneif it is constant | 
| set_alias | exported the last variable/constant within the Scopeby name | 
| iter,iter_raw,IntoIterator::into_iter | get an iterator to the variables/constants within the Scope | 
| Extend::extend | add variables/constants to the Scope | 
For details on the Scope API, refer to the
documentation online.
Serializing/Deserializing
With the serde feature, Scope is serializable and deserializable via
serde.
Custom types stored in the Scope, however, are serialized as full type-name strings.
Data in custom types are not serialized.
Example
In the following example, a Scope is created with a few initialized variables, then it is threaded
through multiple evaluations.
use rhai::{Engine, Scope, EvalAltResult};
let engine = Engine::new();
// First create the state
let mut scope = Scope::new();
// Then push (i.e. add) some initialized variables into the state.
// Remember the system number types in Rhai are i64 (i32 if 'only_i32')
// and f64 (f32 if 'f32_float').
// Better stick to them or it gets hard working with the script.
scope.push("y", 42_i64)
     .push("z", 999_i64)
     .push_constant("MY_NUMBER", 123_i64)       // constants can also be added
     .set_value("s", "hello, world!");          // 'set_value' adds a new variable when one doesn't exist
// First invocation
engine.run_with_scope(&mut scope, 
"
    let x = 4 + 5 - y + z + MY_NUMBER + s.len;
    y = 1;
")?;
// Second invocation using the same state.
// Notice that the new variable 'x', defined previously, is still here.
let result = engine.eval_with_scope::<i64>(&mut scope, "x + y")?;
println!("result: {result}");                   // prints 1103
// Variable y is changed in the script - read it with 'get_value'
assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 1);
// We can modify scope variables directly with 'set_value'
scope.set_value("y", 42_i64);
assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 42);Engine API Using Scope
Engine API methods that accept a Scope parameter all end in _with_scope, making that
Scope (and everything inside it) available to the script:
| EngineAPI | Not available under | 
|---|---|
| Engine::eval_with_scope | |
| Engine::eval_ast_with_scope | |
| Engine::eval_file_with_scope | no_std | 
| Engine::eval_expression_with_scope | |
| Engine::run_with_scope | |
| Engine::run_ast_with_scope | |
| Engine::run_file_with_scope | no_std | 
| Engine::compile_file_with_scope | no_std | 
| Engine::compile_expression_with_scope | 
Variables or constants defined at the global level of a script persist inside the custom Scope
even after the script ends.
let mut scope = Scope::new();
engine.run_with_scope(&mut scope, "let x = 42;")?;
// Variable 'x' stays inside the custom scope!
engine.run_with_scope(&mut scope, "print(x);")?;    //  prints 42Due to variable shadowing, new variables/constants are simply added on top of
existing ones (even when they already exist), so care must be taken that new variables/constants
inside the custom Scope do not grow without bounds.
let mut scope = Scope::new();
// Don't do this - this creates 1 million variables named 'x'
//                 inside 'scope'!!!
for _ in 0..1_000_000 {
    engine.run_with_scope(&mut scope, "let x = 42;")?;
}
// The 'scope' contains a LOT of variables...
assert_eq!(scope.len(), 1_000_000);
// Variable 'x' stays inside the custom scope!
engine.run_with_scope(&mut scope, "print(x);")?;    //  prints 42In order to remove variables or constants introduced by a script, use the rewind method.
// Run a million times
for _ in 0..1_000_000 {
    // Save the current size of the 'scope'
    let orig_scope_size = scope.len();
    engine.run_with_scope(&mut scope, "let x = 42;")?;
    // Rewind the 'scope' to the original size
    scope.rewind(orig_scope_size);
}
// The 'scope' is empty
assert_eq!(scope.len(), 0);
// Variable 'x' is no longer inside 'scope'!
engine.run_with_scope(&mut scope, "print(x);")?;    //  error: variable 'x' not found