reorganize module
This commit is contained in:
		@@ -1,62 +0,0 @@
 | 
			
		||||
Blocking/Async Function Calls
 | 
			
		||||
=============================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish danger.small "Warning: Async and scripting don't mix well"
 | 
			
		||||
 | 
			
		||||
Otherwise, you reinvent the [_Callback Hell_](https://en.wiktionary.org/wiki/callback_hell)
 | 
			
		||||
which is JavaScript before all the async extensions.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenarios"
 | 
			
		||||
 | 
			
		||||
* A system's API contains async functions.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* This pattern is based upon the _[Multi-Threaded Synchronization](multi-threading.md)_ pattern.
 | 
			
		||||
 | 
			
		||||
* An independent thread is used to run the scripting [`Engine`].
 | 
			
		||||
 | 
			
		||||
* An MPSC channel (or any other appropriate synchronization primitive) is used to send function call
 | 
			
		||||
  arguments, packaged as a message, to another Rust thread that will perform the actual async calls.
 | 
			
		||||
 | 
			
		||||
* Results are marshaled back to the [`Engine`] thread via another MPSC channel.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
```admonish info.side "See also"
 | 
			
		||||
 | 
			
		||||
See the _[Multi-Threaded Synchronization](multi-threading.md)_ pattern.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
1. Spawn a thread to run the scripting [`Engine`].  Usually the [`sync`] feature is
 | 
			
		||||
   _NOT_ used for this pattern.
 | 
			
		||||
 | 
			
		||||
2. Spawn another thread (the `worker` thread) that can perform the actual async calls in Rust.
 | 
			
		||||
   This thread may actually be the main thread of the program.
 | 
			
		||||
 | 
			
		||||
3. Create a pair of MPSC channels (named `command` and `reply` below) for full-duplex
 | 
			
		||||
   communications between the two threads.
 | 
			
		||||
 | 
			
		||||
4. Register async API function to the [`Engine`] with a closure that captures the MPSC end-points.
 | 
			
		||||
 | 
			
		||||
5. If there are more than one async function, the receive end-point on the `reply` channel can simply be cloned.
 | 
			
		||||
   The send end-point on the `command` channel can be wrapped in an `Arc<Mutex<Channel>>` for shared access.
 | 
			
		||||
 | 
			
		||||
6. In the async function, the name of the function and call arguments are serialized into JSON
 | 
			
		||||
   (or any appropriate message format) and sent to `command` channel, where they'll be removed
 | 
			
		||||
   by the `worker` thread and the appropriate async function called.
 | 
			
		||||
 | 
			
		||||
7. The [`Engine`] blocks on the function call, waiting for a reply message on the `reply` channel.
 | 
			
		||||
 | 
			
		||||
8. When the async function call complete on the `worker` thread, the result is sent back to
 | 
			
		||||
   the [`Engine`] thread via the `reply` channel.
 | 
			
		||||
 | 
			
		||||
9. After the result is obtained from the `reply` channel, the [`Engine`] returns it as the return value
 | 
			
		||||
   of the function call, ending the block and continuing evaluation.
 | 
			
		||||
@@ -1,307 +0,0 @@
 | 
			
		||||
Builder Pattern / Fluent API
 | 
			
		||||
============================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* An API uses the [Builder Pattern](https://en.wikipedia.org/wiki/Builder_pattern) or a [fluent API](https://en.wikipedia.org/wiki/Fluent_interface).
 | 
			
		||||
 | 
			
		||||
* The builder type is not necessarily `Clone`.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Wrap the builder type in shared interior mutability (aka `Rc<RefCell<T>>` or `Arc<RwLock<T>>`).
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Fluent API"
 | 
			
		||||
 | 
			
		||||
This same pattern can be used to implement any [_fluent API_](https://en.wikipedia.org/wiki/Fluent_interface).
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation With Clonable Builder Type
 | 
			
		||||
-----------------------------------------
 | 
			
		||||
 | 
			
		||||
This assumes that the builder type implements `Clone`.
 | 
			
		||||
This is the most common scenario.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
/// Builder for `Foo` instances.
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct FooBuilder {
 | 
			
		||||
    /// The `foo` option.
 | 
			
		||||
    foo: i64,
 | 
			
		||||
    /// The `bar` option.
 | 
			
		||||
    bar: bool,
 | 
			
		||||
    /// The `baz` option.
 | 
			
		||||
    baz: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// `FooBuilder` API which uses moves.
 | 
			
		||||
impl FooBuilder {
 | 
			
		||||
    /// Creates a new builder for `Foo`.
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self { foo: 0, bar: false, baz: String::new() }
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `foo` option.
 | 
			
		||||
    pub fn with_foo(mut self, foo: i64) -> Self {
 | 
			
		||||
        self.foo = foo;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `bar` option.
 | 
			
		||||
    pub fn with_bar(mut self, bar: bool) -> Self {
 | 
			
		||||
        self.bar = bar;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `baz` option.
 | 
			
		||||
    pub fn with_baz(mut self, baz: &str) -> Self {
 | 
			
		||||
        self.baz = baz.to_string();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    /// Builds the `Foo` instance.
 | 
			
		||||
    pub fn build(self) -> Foo {
 | 
			
		||||
        Foo { foo: self.foo, bar: self.bar, baz: self.baz }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
engine
 | 
			
		||||
    .register_fn("get_foo", FooBuilder::new)
 | 
			
		||||
    .register_fn("with_foo", FooBuilder::with_foo)
 | 
			
		||||
    .register_fn("with_bar", FooBuilder::with_bar)
 | 
			
		||||
    .register_fn("with_baz", FooBuilder::with_baz)
 | 
			
		||||
    .register_fn("create", FooBuilder::build);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation With Mutable Reference
 | 
			
		||||
-------------------------------------
 | 
			
		||||
 | 
			
		||||
This assumes that the builder type's API uses mutable references.
 | 
			
		||||
The builder type does not need to implement `Clone`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::plugin::*;
 | 
			
		||||
 | 
			
		||||
/// Builder for `Foo` instances.
 | 
			
		||||
/// Notice that this type does not need to be `Clone`.
 | 
			
		||||
pub struct FooBuilder {
 | 
			
		||||
    /// The `foo` option.
 | 
			
		||||
    foo: i64,
 | 
			
		||||
    /// The `bar` option.
 | 
			
		||||
    bar: bool,
 | 
			
		||||
    /// The `baz` option.
 | 
			
		||||
    baz: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Builder type API uses mutable references.
 | 
			
		||||
impl FooBuilder {
 | 
			
		||||
    /// Creates a new builder for `Foo`.
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self { foo: 0, bar: false, baz: String::new() }
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `foo` option.
 | 
			
		||||
    pub fn with_foo(&mut self, foo: i64) -> &mut Self {
 | 
			
		||||
        self.foo = foo; self
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `bar` option.
 | 
			
		||||
    pub fn with_bar(&mut self, bar: bool) -> &mut Self {
 | 
			
		||||
        self.bar = bar; self
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `baz` option.
 | 
			
		||||
    pub fn with_baz(&mut self, baz: &str) -> &mut Self {
 | 
			
		||||
        self.baz = baz.to_string(); self
 | 
			
		||||
    }
 | 
			
		||||
    /// Builds the `Foo` instance.
 | 
			
		||||
    pub fn build(&self) -> Foo {
 | 
			
		||||
        Foo { foo: self.foo, bar: self.bar, baz: self.baz.clone() }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Builder for `Foo`.
 | 
			
		||||
#[export_module]
 | 
			
		||||
pub mod foo_builder {
 | 
			
		||||
    use super::{Foo, FooBuilder as BuilderImpl};
 | 
			
		||||
    use std::cell::RefCell;
 | 
			
		||||
    use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
    /// The builder for `Foo`.
 | 
			
		||||
    // This type is `Clone`.
 | 
			
		||||
    pub type FooBuilder = Rc<RefCell<super::BuilderImpl>>;
 | 
			
		||||
 | 
			
		||||
    /// Creates a new builder for `Foo`.
 | 
			
		||||
    pub fn default() -> FooBuilder {
 | 
			
		||||
        Rc::new(RefCell::new(BuilderImpl::new()))
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `foo` option.
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn with_foo(builder: &mut FooBuilder, foo: i64) -> FooBuilder {
 | 
			
		||||
        builder.set_foo(foo);
 | 
			
		||||
        builder.clone()
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `bar` option.
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn with_bar(builder: &mut FooBuilder, bar: bool) -> FooBuilder {
 | 
			
		||||
        builder.set_bar(bar);
 | 
			
		||||
        builder.clone()
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `baz` option.
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn with_baz(builder: &mut FooBuilder, baz: &str) -> FooBuilder {
 | 
			
		||||
        builder.set_baz(baz);
 | 
			
		||||
        builder.clone()
 | 
			
		||||
    }
 | 
			
		||||
    /// Builds the `Foo` instance.
 | 
			
		||||
    #[rhai_fn(global, pure)]
 | 
			
		||||
    pub fn create(builder: &mut FooBuilder) -> Foo {
 | 
			
		||||
        builder.borrow().build()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation With Moves
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
What if the builder type's API relies on moves instead of mutable references?
 | 
			
		||||
And the builder type does not implement `Clone`?
 | 
			
		||||
 | 
			
		||||
Not too worry: the following trick has you covered!
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::plugin::*;
 | 
			
		||||
 | 
			
		||||
/// Builder for `Foo` instances.
 | 
			
		||||
/// Notice that this type does not need to be `Clone`.
 | 
			
		||||
pub struct FooBuilder {
 | 
			
		||||
    /// The `foo` option.
 | 
			
		||||
    foo: i64,
 | 
			
		||||
    /// The `bar` option.
 | 
			
		||||
    bar: bool,
 | 
			
		||||
    /// The `baz` option.
 | 
			
		||||
    baz: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// `FooBuilder` API which uses moves.
 | 
			
		||||
impl FooBuilder {
 | 
			
		||||
    /// Creates a new builder for `Foo`.
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self { foo: 0, bar: false, baz: String::new() }
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `foo` option.
 | 
			
		||||
    pub fn with_foo(mut self, foo: i64) -> Self {
 | 
			
		||||
        self.foo = foo;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `bar` option.
 | 
			
		||||
    pub fn with_bar(mut self, bar: bool) -> Self {
 | 
			
		||||
        self.bar = bar;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `baz` option.
 | 
			
		||||
    pub fn with_baz(mut self, baz: &str) -> Self {
 | 
			
		||||
        self.baz = baz.to_string();
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    /// Builds the `Foo` instance.
 | 
			
		||||
    pub fn build(self) -> Foo {
 | 
			
		||||
        Foo { foo: self.foo, bar: self.bar, baz: self.baz }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Builder for `Foo`.
 | 
			
		||||
#[export_module]
 | 
			
		||||
pub mod foo_builder {
 | 
			
		||||
    use super::{Foo, FooBuilder as BuilderImpl};
 | 
			
		||||
    use std::cell::RefCell;
 | 
			
		||||
    use std::mem;
 | 
			
		||||
    use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
    /// The builder for `Foo`.
 | 
			
		||||
    // This type is `Clone`.
 | 
			
		||||
    // An `Option` is used for easy extraction of the builder type.
 | 
			
		||||
    // If it is `None` then the builder is already consumed.
 | 
			
		||||
    pub type FooBuilder = Rc<RefCell<Option<BuilderImpl>>>;
 | 
			
		||||
 | 
			
		||||
    /// Creates a new builder for `Foo`.
 | 
			
		||||
    pub fn default() -> FooBuilder {
 | 
			
		||||
        Rc::new(RefCell::new(Some(BuilderImpl::new())))
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `foo` option.
 | 
			
		||||
    #[rhai_fn(return_raw, global, pure)]
 | 
			
		||||
    pub fn with_foo(builder: &mut FooBuilder, foo: i64) -> Result<FooBuilder, Box<EvalAltResult>> {
 | 
			
		||||
        let b = &mut *builder.borrow_mut();
 | 
			
		||||
 | 
			
		||||
        if let Some(obj) = mem::take(b) {
 | 
			
		||||
            *b = Some(obj.with_foo(foo));
 | 
			
		||||
            Ok(builder.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err("Builder is already consumed".into())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `bar` option.
 | 
			
		||||
    #[rhai_fn(return_raw, global, pure)]
 | 
			
		||||
    pub fn with_bar(builder: &mut FooBuilder, bar: bool) -> Result<FooBuilder, Box<EvalAltResult>> {
 | 
			
		||||
        let b = &mut *builder.borrow_mut();
 | 
			
		||||
 | 
			
		||||
        if let Some(obj) = mem::take(b) {
 | 
			
		||||
            *b = Some(obj.with_bar(bar));
 | 
			
		||||
            Ok(builder.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err("Builder is already consumed".into())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// Sets the `baz` option.
 | 
			
		||||
    #[rhai_fn(return_raw, global, pure)]
 | 
			
		||||
    pub fn with_baz(builder: &mut FooBuilder, baz: &str) -> Result<FooBuilder, Box<EvalAltResult>> {
 | 
			
		||||
        let b = &mut *builder.borrow_mut();
 | 
			
		||||
 | 
			
		||||
        if let Some(obj) = mem::take(b) {
 | 
			
		||||
            *b = Some(obj.with_baz(baz));
 | 
			
		||||
            Ok(builder.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err("Builder is already consumed".into())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// Builds the `Foo` instance.
 | 
			
		||||
    #[rhai_fn(return_raw, global, pure)]
 | 
			
		||||
    pub fn create(builder: &mut FooBuilder) -> Result<Foo, Box<EvalAltResult>> {
 | 
			
		||||
        let b = &mut *builder.borrow_mut();
 | 
			
		||||
 | 
			
		||||
        if let Some(obj) = mem::take(b) {
 | 
			
		||||
            Ok(obj.build())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err("Builder is already consumed".into())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Usage
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
It is easy to see that the Rhai script API mirrors the Rust API almost perfectly.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
┌──────┐
 | 
			
		||||
│ Rust │
 | 
			
		||||
└──────┘
 | 
			
		||||
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
engine.register_static_module("Foo", exported_module!(foo_builder).into());
 | 
			
		||||
 | 
			
		||||
let foo = FooBuilder::new().with_foo(42).with_bar(true).with_baz("Hello").build();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌─────────────┐
 | 
			
		||||
│ Rhai script │
 | 
			
		||||
└─────────────┘
 | 
			
		||||
 | 
			
		||||
let foo = Foo::default().with_foo(42).with_bar(true).with_baz("Hello").create();
 | 
			
		||||
```
 | 
			
		||||
@@ -1,171 +0,0 @@
 | 
			
		||||
Loadable Configuration
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system where settings and configurations are complex and logic-driven.
 | 
			
		||||
 | 
			
		||||
* Where said system is too complex to configure via standard configuration file formats such as
 | 
			
		||||
  `JSON`, `TOML` or `YAML`.
 | 
			
		||||
 | 
			
		||||
* The system is complex enough to require a full programming language to configure.
 | 
			
		||||
  Essentially _configuration by code_.
 | 
			
		||||
 | 
			
		||||
* Yet the configuration must be flexible, late-bound and dynamically loadable, just like a
 | 
			
		||||
  configuration file.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Leverage the loadable [modules] of Rhai.  The [`no_module`] feature must not be on.
 | 
			
		||||
 | 
			
		||||
* Expose the configuration API.  Use separate scripts to configure that API.
 | 
			
		||||
  Dynamically load scripts via the `import` statement.
 | 
			
		||||
 | 
			
		||||
* Leverage [function overloading] to simplify the API design.
 | 
			
		||||
 | 
			
		||||
* Since Rhai is _sand-boxed_, it cannot mutate the environment.  To modify the external
 | 
			
		||||
  configuration object via an API, it must be wrapped in a `RefCell` (or `RwLock`/`Mutex` for
 | 
			
		||||
  [`sync`]) and shared to the [`Engine`].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
### Configuration type
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
#[derive(Debug, Clone, Default)]
 | 
			
		||||
struct Config {
 | 
			
		||||
    id: String,
 | 
			
		||||
    some_field: i64,
 | 
			
		||||
    some_list: Vec<String>,
 | 
			
		||||
    some_map: HashMap<String, bool>,
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Make shared object
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
type SharedConfig = Rc<RefCell<Config>>;
 | 
			
		||||
 | 
			
		||||
let config = SharedConfig::default();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
or in multi-threaded environments with the [`sync`] feature, use one of the following:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
type SharedConfig = Arc<RwLock<Config>>;
 | 
			
		||||
 | 
			
		||||
type SharedConfig = Arc<Mutex<Config>>;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Register config API
 | 
			
		||||
 | 
			
		||||
The trick to building a Config API is to clone the shared configuration object and move it into each
 | 
			
		||||
function registration via a closure.
 | 
			
		||||
 | 
			
		||||
Therefore, it is not possible to use a [plugin module] to achieve this, and each function must be
 | 
			
		||||
registered one after another.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Notice 'move' is used to move the shared configuration object into the closure.
 | 
			
		||||
let cfg = config.clone();
 | 
			
		||||
engine.register_fn("config_set_id", move |id: String| cfg.borrow_mut().id = id);
 | 
			
		||||
 | 
			
		||||
let cfg = config.clone();
 | 
			
		||||
engine.register_fn("config_get_id", move || cfg.borrow().id.clone());
 | 
			
		||||
 | 
			
		||||
let cfg = config.clone();
 | 
			
		||||
engine.register_fn("config_set", move |value: i64| cfg.borrow_mut().some_field = value);
 | 
			
		||||
 | 
			
		||||
// Remember Rhai functions can be overloaded when designing the API.
 | 
			
		||||
 | 
			
		||||
let cfg = config.clone();
 | 
			
		||||
engine.register_fn("config_add", move |value: String|
 | 
			
		||||
    cfg.borrow_mut().some_list.push(value)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
let cfg = config.clone();
 | 
			
		||||
engine.register_fn("config_add", move |values: &mut Array|
 | 
			
		||||
    cfg.borrow_mut().some_list.extend(values.into_iter().map(|v| v.to_string()))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
let cfg = config.clone();
 | 
			
		||||
engine.register_fn("config_add", move |key: String, value: bool|
 | 
			
		||||
    cfg.borrow_mut().some_map.insert(key, value)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
let cfg = config.clone();
 | 
			
		||||
engine.register_fn("config_contains", move |value: String|
 | 
			
		||||
    cfg.borrow().some_list.contains(&value)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
let cfg = config.clone();
 | 
			
		||||
engine.register_fn("config_is_set", move |value: String|
 | 
			
		||||
    cfg.borrow().some_map.get(&value).cloned().unwrap_or(false)
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Configuration script
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
┌────────────────┐
 | 
			
		||||
│ my_config.rhai │
 | 
			
		||||
└────────────────┘
 | 
			
		||||
 | 
			
		||||
config_set_id("hello");
 | 
			
		||||
 | 
			
		||||
config_add("foo");          // add to list
 | 
			
		||||
config_add("bar", true);    // add to map
 | 
			
		||||
 | 
			
		||||
if config_contains("hey") || config_is_set("hey") {
 | 
			
		||||
    config_add("baz", false);   // add to map
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Load the configuration
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
import "my_config";         // run configuration script without creating a module
 | 
			
		||||
 | 
			
		||||
let id = config_get_id();
 | 
			
		||||
 | 
			
		||||
id == "hello";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Consider a Custom Syntax
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
This is probably one of the few scenarios where a [custom syntax] can be recommended.
 | 
			
		||||
 | 
			
		||||
A properly-designed [custom syntax] can make the configuration file clean, simple to write,
 | 
			
		||||
easy to understand and quick to modify.
 | 
			
		||||
 | 
			
		||||
For example, the above configuration example may be expressed by this custom syntax:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
┌────────────────┐
 | 
			
		||||
│ my_config.rhai │
 | 
			
		||||
└────────────────┘
 | 
			
		||||
 | 
			
		||||
// Configure ID
 | 
			
		||||
id "hello";
 | 
			
		||||
 | 
			
		||||
// Add to list
 | 
			
		||||
list + "foo";
 | 
			
		||||
 | 
			
		||||
// Add to map
 | 
			
		||||
map "bar" => true;
 | 
			
		||||
 | 
			
		||||
if config contains "hey" || config is_set "hey" {
 | 
			
		||||
    map "baz" => false;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Notice that `contains` and `is_set` may also be implemented as a [custom operator].
 | 
			
		||||
@@ -1,168 +0,0 @@
 | 
			
		||||
Global Constants
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* Script has a lot of duplicated [constants] used inside [functions].
 | 
			
		||||
 | 
			
		||||
* For easier management, [constants] are declared at the top of the script.
 | 
			
		||||
 | 
			
		||||
* As Rhai [functions] are pure, they cannot access [constants] declared at global level
 | 
			
		||||
  except through [`global`].
 | 
			
		||||
 | 
			
		||||
* Sprinkling large number of [`global::CONSTANT`][`global`] throughout the script makes
 | 
			
		||||
  it slow and cumbersome.
 | 
			
		||||
 | 
			
		||||
* Using [`global`] or a [variable resolver] defeats
 | 
			
		||||
  [constants propagation]({{rootUrl}}/engine/optimize/constants.md) in [script optimization].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* The key to global [constants] is to use them to [optimize][script optimization] a script.
 | 
			
		||||
  Otherwise, it would be just as simple to pass the constants into a custom [`Scope`] instead.
 | 
			
		||||
 | 
			
		||||
* The script is first compiled into an [`AST`], and all [constants] are extracted.
 | 
			
		||||
 | 
			
		||||
* The [constants] are then supplied to [re-optimize][script optimization] the [`AST`].
 | 
			
		||||
 | 
			
		||||
* This pattern also works under [_Strict Variables Mode_][strict variables].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
Assume that the following Rhai script needs to work (but it doesn't).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// These are constants
 | 
			
		||||
 | 
			
		||||
const FOO = 1;
 | 
			
		||||
const BAR = 123;
 | 
			
		||||
const MAGIC_NUMBER = 42;
 | 
			
		||||
 | 
			
		||||
fn get_magic() {
 | 
			
		||||
    MAGIC_NUMBER        // <- oops! 'MAGIC_NUMBER' not found!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn calc_foo(x) {
 | 
			
		||||
    x * global::FOO     // <- works but cumbersome; not desirable!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let magic = get_magic() * BAR;
 | 
			
		||||
 | 
			
		||||
let x = calc_foo(magic);
 | 
			
		||||
 | 
			
		||||
print(x);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Step 1 – Compile Script into `AST`
 | 
			
		||||
----------------------------------------
 | 
			
		||||
 | 
			
		||||
Compile the script into [`AST`] form.
 | 
			
		||||
 | 
			
		||||
Normally, it is useful to disable [optimizations][script optimization] at this stage since
 | 
			
		||||
the [`AST`] will be re-optimized later.
 | 
			
		||||
 | 
			
		||||
[_Strict Variables Mode_][strict variables] must be OFF for this to work.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Turn Strict Variables Mode OFF (if necessary)
 | 
			
		||||
engine.set_strict_variables(false);
 | 
			
		||||
 | 
			
		||||
// Turn optimizations OFF
 | 
			
		||||
engine.set_optimization_level(OptimizationLevel::None);
 | 
			
		||||
 | 
			
		||||
let ast = engine.compile("...")?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Step 2 – Extract Constants
 | 
			
		||||
--------------------------------
 | 
			
		||||
 | 
			
		||||
Use [`AST::iter_literal_variables`](https://docs.rs/rhai/{{version}}/rhai/struct.AST.html#method.iter_literal_variables)
 | 
			
		||||
to extract top-level [constants] from the [`AST`].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let mut scope = Scope::new();
 | 
			
		||||
 | 
			
		||||
// Extract all top-level constants without running the script
 | 
			
		||||
ast.iter_literal_variables(true, false).for_each(|(name, _, value)|
 | 
			
		||||
    scope.push_constant(name, value);
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// 'scope' now contains: FOO, BAR, MAGIC_NUMBER
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Step 3a – Propagate Constants
 | 
			
		||||
-----------------------------------
 | 
			
		||||
 | 
			
		||||
[Re-optimize][script optimization] the [`AST`] using the new constants.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Turn optimization back ON
 | 
			
		||||
engine.set_optimization_level(OptimizationLevel::Simple);
 | 
			
		||||
 | 
			
		||||
let ast = engine.optimize_ast(&scope, ast, engine.optimization_level());
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Step 3b – Recompile Script (Alternative)
 | 
			
		||||
----------------------------------------------
 | 
			
		||||
 | 
			
		||||
If [_Strict Variables Mode_][strict variables] is used, however, it is necessary to re-compile the
 | 
			
		||||
script in order to detect undefined [variable] usages.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Turn Strict Variables Mode back ON
 | 
			
		||||
engine.set_strict_variables(true);
 | 
			
		||||
 | 
			
		||||
// Turn optimization back ON
 | 
			
		||||
engine.set_optimization_level(OptimizationLevel::Simple);
 | 
			
		||||
 | 
			
		||||
// Re-compile the script using constants in 'scope'
 | 
			
		||||
let ast = engine.compile_with_scope(&scope, "...")?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Step 4 – Run the Script
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
At this step, the [`AST`] is now optimized with constants propagated into all access sites.
 | 
			
		||||
 | 
			
		||||
The script essentially becomes:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// These are constants
 | 
			
		||||
 | 
			
		||||
const FOO = 1;
 | 
			
		||||
const BAR = 123;
 | 
			
		||||
const MAGIC_NUMBER = 42;
 | 
			
		||||
 | 
			
		||||
fn get_magic() {
 | 
			
		||||
    42      // <- constant replaced by value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn calc_foo(x) {
 | 
			
		||||
    x * global::FOO
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let magic = get_magic() * 123;  // <- constant replaced by value
 | 
			
		||||
 | 
			
		||||
let x = calc_foo(magic);
 | 
			
		||||
 | 
			
		||||
print(x);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Run it via `Engine::run_ast` or `Engine::eval_ast`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// The 'scope' is no longer necessary
 | 
			
		||||
engine.run_ast(&ast)?;
 | 
			
		||||
```
 | 
			
		||||
@@ -1,145 +0,0 @@
 | 
			
		||||
Scriptable Control Layer Over Rust Backend
 | 
			
		||||
==========================================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system provides core functionalities, but no driving logic.
 | 
			
		||||
 | 
			
		||||
* The driving logic must be dynamic and hot-loadable.
 | 
			
		||||
 | 
			
		||||
* A script is used to drive the system and provide control intelligence.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Expose a Control API.
 | 
			
		||||
 | 
			
		||||
* Leverage [function overloading] to simplify the API design.
 | 
			
		||||
 | 
			
		||||
* Since Rhai is _[sand-boxed]_, it cannot mutate anything outside of its internal environment.
 | 
			
		||||
  To perform external actions via an API, the actual system must be wrapped in a `RefCell`
 | 
			
		||||
  (or `RwLock`/`Mutex` for [`sync`]) and shared to the [`Engine`].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish danger "Using Rhai for games"
 | 
			
		||||
 | 
			
		||||
Although this usage pattern appears a perfect fit for _game_ logic, avoid writing the _entire game_
 | 
			
		||||
in Rhai.  Performance will not be acceptable.
 | 
			
		||||
 | 
			
		||||
Implement as much functionalities of the game engine in Rust as possible. Rhai integrates well with
 | 
			
		||||
Rust so this is usually not a hinderance.
 | 
			
		||||
 | 
			
		||||
Lift as much out of Rhai as possible. Use Rhai only for the logic that _must_ be dynamic or
 | 
			
		||||
hot-loadable.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
There are two broad ways for Rhai to control an external system, both of which involve wrapping the
 | 
			
		||||
system in a shared, interior-mutated object.
 | 
			
		||||
 | 
			
		||||
This is one way which does not involve exposing the data structures of the external system,
 | 
			
		||||
but only through exposing an abstract API primarily made up of functions.
 | 
			
		||||
 | 
			
		||||
Use this when the API is relatively simple and clean, and the number of functions is small enough.
 | 
			
		||||
 | 
			
		||||
For a complex API involving lots of functions, or an API that has a clear object structure, use the
 | 
			
		||||
[Singleton Command Object]({{rootUrl}}/patterns/singleton.md) pattern instead.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Functional API
 | 
			
		||||
 | 
			
		||||
Assume that a system provides the following functional API:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
struct EnergizerBunny;
 | 
			
		||||
 | 
			
		||||
impl EnergizerBunny {
 | 
			
		||||
    pub fn new () -> Self { ... }
 | 
			
		||||
    pub fn go (&mut self) { ... }
 | 
			
		||||
    pub fn stop (&mut self) { ... }
 | 
			
		||||
    pub fn is_going (&self) { ... }
 | 
			
		||||
    pub fn get_speed (&self) -> i64 { ... }
 | 
			
		||||
    pub fn set_speed (&mut self, speed: i64) { ... }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Wrap API in shared object
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
pub type SharedBunny = Rc<RefCell<EnergizerBunny>>;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
or in multi-threaded environments with the [`sync`] feature, use one of the following:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
pub type SharedBunny = Arc<RwLock<EnergizerBunny>>;
 | 
			
		||||
 | 
			
		||||
pub type SharedBunny = Arc<Mutex<EnergizerBunny>>;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Register control API
 | 
			
		||||
 | 
			
		||||
The trick to building a Control API is to clone the shared API object and
 | 
			
		||||
move it into each function registration via a closure.
 | 
			
		||||
 | 
			
		||||
Therefore, it is not possible to use a [plugin module] to achieve this, and each function must be
 | 
			
		||||
registered one after another.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Notice 'move' is used to move the shared API object into the closure.
 | 
			
		||||
let b = bunny.clone();
 | 
			
		||||
engine.register_fn("bunny_power", move |on: bool| {
 | 
			
		||||
    if on {
 | 
			
		||||
        if b.borrow().is_going() {
 | 
			
		||||
            println!("Still going...");
 | 
			
		||||
        } else {
 | 
			
		||||
            b.borrow_mut().go();
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if b.borrow().is_going() {
 | 
			
		||||
            b.borrow_mut().stop();
 | 
			
		||||
        } else {
 | 
			
		||||
            println!("Already out of battery!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
let b = bunny.clone();
 | 
			
		||||
engine.register_fn("bunny_is_going", move || b.borrow().is_going());
 | 
			
		||||
 | 
			
		||||
let b = bunny.clone();
 | 
			
		||||
engine.register_fn("bunny_get_speed", move ||
 | 
			
		||||
    if b.borrow().is_going() { b.borrow().get_speed() } else { 0 }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
let b = bunny.clone();
 | 
			
		||||
engine.register_fn("bunny_set_speed", move |speed: i64| -> Result<_, Box<EvalAltResult>>
 | 
			
		||||
    if speed <= 0 {
 | 
			
		||||
        return Err("Speed must be positive!".into());
 | 
			
		||||
    } else if speed > 100 {
 | 
			
		||||
        return Err("Bunny will be going too fast!".into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if b.borrow().is_going() {
 | 
			
		||||
        b.borrow_mut().set_speed(speed)
 | 
			
		||||
    } else {
 | 
			
		||||
        return Err("Bunny is not yet going!".into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Use the API
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
if !bunny_is_going() { bunny_power(true); }
 | 
			
		||||
 | 
			
		||||
if bunny_get_speed() > 50 { bunny_set_speed(50); }
 | 
			
		||||
```
 | 
			
		||||
@@ -1,139 +0,0 @@
 | 
			
		||||
Domain-Specific Tools
 | 
			
		||||
=====================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
[bin tool]: {{rootUrl}}/start/bin.md
 | 
			
		||||
[bin tools]: {{rootUrl}}/start/bin.md
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system has a _domain-specific_ API, requiring [custom types] and/or
 | 
			
		||||
  Rust [functions]({{rootUrl}}/rust/functions.md) to be registered and exposed to scripting.
 | 
			
		||||
 | 
			
		||||
* The system's behavior is controlled by Rhai script [functions], such as in the
 | 
			
		||||
  [_Scriptable Event Handler with State_](events.md), [_Control Layer_](control.md) or
 | 
			
		||||
  [_Singleton Command Object_](singleton.md) patterns.
 | 
			
		||||
 | 
			
		||||
* It is desirable to be able to _interactively_ test the system with different scripts,
 | 
			
		||||
  and/or [debug][debugger] them.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Leverage the pre-packaged [bin tools] – it is easy because each of them is
 | 
			
		||||
  one single source file.
 | 
			
		||||
 | 
			
		||||
* Modify the [`Engine`] creation code to include domain-specific registrations.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
### Copy necessary tool source files
 | 
			
		||||
 | 
			
		||||
Each [bin tool] is a single source file.
 | 
			
		||||
 | 
			
		||||
Download the necessary one(s) into the project's `bin` or `example` subdirectory.
 | 
			
		||||
 | 
			
		||||
| Select this source file                             | To make                               |
 | 
			
		||||
| --------------------------------------------------- | ------------------------------------- |
 | 
			
		||||
| [`rhai-run.rs`]({{repoHome}}/src/bin/rhai-run.rs)   | a simple script runner for the system |
 | 
			
		||||
| [`rhai-repl.rs`]({{repoHome}}/src/bin/rhai-repl.rs) | an interactive REPL for the system    |
 | 
			
		||||
| [`rhai-dbg.rs`]({{repoHome}}/src/bin/rhai-dbg.rs)   | a script debugger for the system      |
 | 
			
		||||
 | 
			
		||||
#### Example
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
rhai-run.rs -> /path/to/my_project/examples/test.rs
 | 
			
		||||
rhai-repl.rs -> /path/to/my_project/examples/repl.rs
 | 
			
		||||
rhai-dbg.rs -> /path/to/my_project/examples/db.rs
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Leverage `Engine` configuration code in the project
 | 
			
		||||
 | 
			
		||||
Assume the project already contains configuration code for a customized [`Engine`].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::Engine;
 | 
			
		||||
use rhai::plugin::*;
 | 
			
		||||
 | 
			
		||||
// Domain-specific data types
 | 
			
		||||
use my_project::*;
 | 
			
		||||
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod my_domain_api {
 | 
			
		||||
            :
 | 
			
		||||
            :
 | 
			
		||||
    // plugin module
 | 
			
		||||
            :
 | 
			
		||||
            :
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function creates a new Rhai scripting engine and
 | 
			
		||||
// configures it properly
 | 
			
		||||
fn create_scripting_engine(config: MySystemConfig) -> Engine {
 | 
			
		||||
    let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
    // Register domain-specific API into the Engine
 | 
			
		||||
    engine.register_type_with_name::<MyObject>("MyObject")
 | 
			
		||||
          .register_type_with_name::<MyOtherObject>("MyOtherObject")
 | 
			
		||||
          .register_fn(...)
 | 
			
		||||
          .register_fn(...)
 | 
			
		||||
          .register_fn(...)
 | 
			
		||||
                    :
 | 
			
		||||
                    :
 | 
			
		||||
        // register API functions
 | 
			
		||||
                    :
 | 
			
		||||
                    :
 | 
			
		||||
          .register_get_set(...)
 | 
			
		||||
          .register_index_get_set(...)
 | 
			
		||||
          .register_fn(...);
 | 
			
		||||
 | 
			
		||||
    // Plugin modules can be used to easily and quickly
 | 
			
		||||
    // register an entire API
 | 
			
		||||
    engine.register_global_module(exported_module!(my_domain_api).into());
 | 
			
		||||
 | 
			
		||||
    // Configuration options in 'MySystemConfig' may be used
 | 
			
		||||
    // to control the Engine's behavior
 | 
			
		||||
    if config.strict_mode {
 | 
			
		||||
        engine.set_strict_variables(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Return the scripting engine
 | 
			
		||||
    engine
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Modify `Engine` creation
 | 
			
		||||
 | 
			
		||||
Each [bin tool] contains a line that creates the main script [`Engine`].
 | 
			
		||||
 | 
			
		||||
Modify it to call the project's creation function.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Initialize scripting engine
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Modify to this:
 | 
			
		||||
let mut engine = create_scripting_engine(my_config);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Make sure that Rhai has the appropriate feature(s)
 | 
			
		||||
 | 
			
		||||
In the project's `Cargo.toml`, specify the Rhai dependency with `bin-features`.
 | 
			
		||||
 | 
			
		||||
```toml
 | 
			
		||||
[dependencies]
 | 
			
		||||
rhai = { version = "{{version}}", features = ["bin-features"] }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Rebuild
 | 
			
		||||
 | 
			
		||||
Rebuild the project, which should automatically build all the customized tools.
 | 
			
		||||
 | 
			
		||||
### Run tools
 | 
			
		||||
 | 
			
		||||
Each customized tool now has access to the entire domain-specific API.
 | 
			
		||||
Functions and [custom types] can be used in REPL, [debugging][debugger] etc.
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
Dynamic Constants Provider
 | 
			
		||||
==========================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system has a _large_ number of constants, but only a minor set will be used by any script.
 | 
			
		||||
 | 
			
		||||
* The system constants are expensive to load.
 | 
			
		||||
 | 
			
		||||
* The system constants set is too massive to push into a custom [`Scope`].
 | 
			
		||||
 | 
			
		||||
* The values of system constants are volatile and call-dependent.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Use a [variable resolver] to intercept variable access.
 | 
			
		||||
 | 
			
		||||
* Only load a variable when it is being used.
 | 
			
		||||
 | 
			
		||||
* Perform a lookup based on variable name, and provide the correct data value.
 | 
			
		||||
 | 
			
		||||
* May even perform back-end network access or look up the latest value from a database.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Create shared data provider.
 | 
			
		||||
// Assume that SystemValuesProvider::get(&str) -> Option<value> gets a value.
 | 
			
		||||
let provider = Arc::new(SystemValuesProvider::new());
 | 
			
		||||
 | 
			
		||||
// Clone the shared provider
 | 
			
		||||
let db = provider.clone();
 | 
			
		||||
 | 
			
		||||
// Register a variable resolver.
 | 
			
		||||
// Move the shared provider into the closure.
 | 
			
		||||
engine.on_var(move |name, _, _| Ok(db.get(name).map(Dynamic::from)));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish note.small "Values are constants"
 | 
			
		||||
 | 
			
		||||
All values provided by a [variable resolver] are _[constants]_ due to their dynamic nature.
 | 
			
		||||
They cannot be assigned to.
 | 
			
		||||
 | 
			
		||||
In order to change values in an external system, register a dedicated API for that purpose.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,281 +0,0 @@
 | 
			
		||||
Working With Rust Enums
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
```admonish question.side.wide "Why enums are hard"
 | 
			
		||||
 | 
			
		||||
Rust enum variants are not considered separate types.
 | 
			
		||||
 | 
			
		||||
Although Rhai integrates fine with Rust enums (treated transparently as [custom types]),
 | 
			
		||||
it is impossible (short of registering a complete API) to distinguish between individual
 | 
			
		||||
variants and to extract internal data from them.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Enums in Rust can hold data and are typically used with _pattern matching_.
 | 
			
		||||
 | 
			
		||||
Unlike Rust, Rhai does not have built-in pattern matching capabilities, so working with enum
 | 
			
		||||
variants that contain embedded data is not an easy proposition.
 | 
			
		||||
 | 
			
		||||
Since Rhai is dynamic and [variables] can hold any type of data, they are essentially enums
 | 
			
		||||
by nature.
 | 
			
		||||
 | 
			
		||||
Multiple distinct types can be stored in a single [`Dynamic`] without merging them into an enum
 | 
			
		||||
as variants.
 | 
			
		||||
 | 
			
		||||
This section outlines a number of possible solutions to work with Rust enums.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Simulate an Enum API
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
A [plugin module] is extremely handy in creating an entire API for a custom enum type.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::plugin::*;
 | 
			
		||||
use rhai::{Dynamic, Engine, EvalAltResult};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
 | 
			
		||||
enum MyEnum {
 | 
			
		||||
    Foo,
 | 
			
		||||
    Bar(i64),
 | 
			
		||||
    Baz(String, bool),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a plugin module with functions constructing the 'MyEnum' variants
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod MyEnumModule {
 | 
			
		||||
    // Constructors for 'MyEnum' variants
 | 
			
		||||
 | 
			
		||||
    /// `MyEnum::Foo` with no inner data.
 | 
			
		||||
    pub const Foo: MyEnum = MyEnum::Foo;
 | 
			
		||||
 | 
			
		||||
    /// `MyEnum::Bar(value)`
 | 
			
		||||
    pub fn Bar(value: i64) -> MyEnum { MyEnum::Bar(value) }
 | 
			
		||||
 | 
			
		||||
    /// `MyEnum::Baz(name, flag)`
 | 
			
		||||
    pub fn Baz(name: String, flag: bool) -> MyEnum { MyEnum::Baz(name, flag) }
 | 
			
		||||
 | 
			
		||||
    /// Return the current variant of `MyEnum`.
 | 
			
		||||
    #[rhai_fn(global, get = "enum_type", pure)]
 | 
			
		||||
    pub fn get_type(my_enum: &mut MyEnum) -> String {
 | 
			
		||||
        match my_enum {
 | 
			
		||||
            MyEnum::Foo => "Foo".to_string(),
 | 
			
		||||
            MyEnum::Bar(_) => "Bar".to_string(),
 | 
			
		||||
            MyEnum::Baz(_, _) => "Baz".to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the inner value.
 | 
			
		||||
    #[rhai_fn(global, get = "value", pure)]
 | 
			
		||||
    pub fn get_value(my_enum: &mut MyEnum) -> Dynamic {
 | 
			
		||||
        match my_enum {
 | 
			
		||||
            MyEnum::Foo => Dynamic::UNIT,
 | 
			
		||||
            MyEnum::Bar(x) => Dynamic::from(x),
 | 
			
		||||
            MyEnum::Baz(_, f) => Dynamic::from(f),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Access to inner values by position
 | 
			
		||||
 | 
			
		||||
    /// Return the value kept in the first position of `MyEnum`.
 | 
			
		||||
    #[rhai_fn(global, get = "field_0", pure)]
 | 
			
		||||
    pub fn get_field_0(my_enum: &mut MyEnum) -> Dynamic {
 | 
			
		||||
        match my_enum {
 | 
			
		||||
            MyEnum::Foo => Dynamic::UNIT,
 | 
			
		||||
            MyEnum::Bar(x) => Dynamic::from(x),
 | 
			
		||||
            MyEnum::Baz(x, _) => Dynamic::from(x)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// Return the value kept in the second position of `MyEnum`.
 | 
			
		||||
    #[rhai_fn(global, get = "field_1", pure)]
 | 
			
		||||
    pub fn get_field_1(my_enum: &mut MyEnum) -> Dynamic {
 | 
			
		||||
        match my_enum {
 | 
			
		||||
            MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT,
 | 
			
		||||
            MyEnum::Baz(_, x) => Dynamic::from(x)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Printing
 | 
			
		||||
    #[rhai_fn(global, name = "to_string", name = "to_debug", pure)]
 | 
			
		||||
    pub fn to_string(my_enum: &mut MyEnum) -> String {
 | 
			
		||||
        format!("{my_enum:?}")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // '==' and '!=' operators
 | 
			
		||||
    #[rhai_fn(global, name = "==", pure)]
 | 
			
		||||
    pub fn eq(my_enum: &mut MyEnum, my_enum2: MyEnum) -> bool {
 | 
			
		||||
        my_enum == &my_enum2
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(global, name = "!=", pure)]
 | 
			
		||||
    pub fn neq(my_enum: &mut MyEnum, my_enum2: MyEnum) -> bool {
 | 
			
		||||
        my_enum != &my_enum2
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Load the module as the module namespace "MyEnum"
 | 
			
		||||
engine.register_type_with_name::<MyEnum>("MyEnum")
 | 
			
		||||
      .register_static_module("MyEnum", exported_module!(MyEnumModule).into());
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
With this API in place, working with enums feels almost the same as in Rust:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = MyEnum::Foo;
 | 
			
		||||
 | 
			
		||||
let y = MyEnum::Bar(42);
 | 
			
		||||
 | 
			
		||||
let z = MyEnum::Baz("hello", true);
 | 
			
		||||
 | 
			
		||||
x == MyEnum::Foo;
 | 
			
		||||
 | 
			
		||||
y != MyEnum::Bar(0);
 | 
			
		||||
 | 
			
		||||
// Detect enum types
 | 
			
		||||
 | 
			
		||||
x.enum_type == "Foo";
 | 
			
		||||
 | 
			
		||||
y.enum_type == "Bar";
 | 
			
		||||
 | 
			
		||||
z.enum_type == "Baz";
 | 
			
		||||
 | 
			
		||||
// Extract enum fields
 | 
			
		||||
 | 
			
		||||
x.value == ();
 | 
			
		||||
 | 
			
		||||
y.value == 42;
 | 
			
		||||
 | 
			
		||||
z.value == ();
 | 
			
		||||
 | 
			
		||||
x.name == ();
 | 
			
		||||
 | 
			
		||||
y.name == ();
 | 
			
		||||
 | 
			
		||||
z.name == "hello";
 | 
			
		||||
 | 
			
		||||
y.field_0 == 42;
 | 
			
		||||
 | 
			
		||||
y.field_1 == ();
 | 
			
		||||
 | 
			
		||||
z.field_0 == "hello";
 | 
			
		||||
 | 
			
		||||
z.field_1 == true;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.small "Tip: Use a macro"
 | 
			
		||||
 | 
			
		||||
For enums containing only variants with no inner data, it is convenient to use a simple macro to create
 | 
			
		||||
such a [plugin module].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// The 'create_enum_module!' macro
 | 
			
		||||
macro_rules! create_enum_module {
 | 
			
		||||
    ($module:ident : $typ:ty => $($variant:ident),+) => {
 | 
			
		||||
        #[export_module]
 | 
			
		||||
        pub mod $module {
 | 
			
		||||
            $(
 | 
			
		||||
                #[allow(non_upper_case_globals)]
 | 
			
		||||
                pub const $variant: $typ = <$typ>::$variant;
 | 
			
		||||
            )*
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The enum
 | 
			
		||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 | 
			
		||||
pub enum MyEnum { Foo, Bar, Baz, Hello, World }
 | 
			
		||||
 | 
			
		||||
// This creates a plugin module called 'my_enum_module'
 | 
			
		||||
expand_enum! { my_enum_module: MyEnum => Foo, Bar, Baz, Hello, World }
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use Enums With `switch`
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
Since enums are internally treated as [custom types], they are not _literals_ and cannot be used as
 | 
			
		||||
a match case in [`switch`] statements.  This is quite a limitation because the equivalent `match`
 | 
			
		||||
statement is commonly used in Rust to work with enums and bind variables to variant-internal data.
 | 
			
		||||
 | 
			
		||||
It is possible, however, to [`switch`] through enum variants based on their types:
 | 
			
		||||
 | 
			
		||||
```c , no_run
 | 
			
		||||
switch my_enum.enum_type {
 | 
			
		||||
  "Foo" => ...,
 | 
			
		||||
  "Bar" => {
 | 
			
		||||
    let value = foo.value;
 | 
			
		||||
    ...
 | 
			
		||||
  }
 | 
			
		||||
  "Baz" => {
 | 
			
		||||
    let name = foo.name;
 | 
			
		||||
    let flag = foo.flag;
 | 
			
		||||
    ...
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use `switch` Through Arrays
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
Another way to work with Rust enums in a [`switch`] statement is through exposing the internal data
 | 
			
		||||
(or at least those that act as effective _discriminants_) of each enum variant as a variable-length
 | 
			
		||||
[array], usually with the name of the variant as the first item for convenience:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::Array;
 | 
			
		||||
 | 
			
		||||
engine.register_get("enum_data", |my_enum: &mut MyEnum| {
 | 
			
		||||
    match my_enum {
 | 
			
		||||
        MyEnum::Foo => vec![ "Foo".into() ] as Array,
 | 
			
		||||
 | 
			
		||||
        // Say, skip the data field because it is not
 | 
			
		||||
        // used as a discriminant
 | 
			
		||||
        MyEnum::Bar(value) => vec![ "Bar".into() ] as Array,
 | 
			
		||||
 | 
			
		||||
        // Say, all fields act as discriminants
 | 
			
		||||
        MyEnum::Baz(name, flag) => vec![
 | 
			
		||||
            "Baz".into(), name.clone().into(), (*flag).into()
 | 
			
		||||
        ] as Array
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then it is a simple matter to match an enum via a [`switch`] expression.
 | 
			
		||||
 | 
			
		||||
```c , no_run
 | 
			
		||||
// Assume 'value' = 'MyEnum::Baz("hello", true)'
 | 
			
		||||
// 'enum_data' creates a variable-length array with 'MyEnum' data
 | 
			
		||||
let x = switch value.enum_data {
 | 
			
		||||
    ["Foo"] => 1,
 | 
			
		||||
    ["Bar"] => value.field_1,
 | 
			
		||||
    ["Baz", "hello", false] => 4,
 | 
			
		||||
    ["Baz", "hello", true] => 5,
 | 
			
		||||
    _ => 9
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
x == 5;
 | 
			
		||||
 | 
			
		||||
// Which is essentially the same as:
 | 
			
		||||
let x = switch [value.type, value.field_0, value.field_1] {
 | 
			
		||||
    ["Foo", (), ()] => 1,
 | 
			
		||||
    ["Bar", 42, ()] => 42,
 | 
			
		||||
    ["Bar", 123, ()] => 123,
 | 
			
		||||
            :
 | 
			
		||||
    ["Baz", "hello", false] => 4,
 | 
			
		||||
    ["Baz", "hello", true] => 5,
 | 
			
		||||
    _ => 9
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Usually, a helper method returns an [array] of values that can uniquely determine the [`switch`] case
 | 
			
		||||
based on actual usage requirements – which means that it probably skips fields that contain
 | 
			
		||||
data instead of discriminants.
 | 
			
		||||
 | 
			
		||||
Then [`switch`] is used to very quickly match through a large number of [array] shapes and jump to the
 | 
			
		||||
appropriate case implementation.
 | 
			
		||||
 | 
			
		||||
Data fields can then be extracted from the enum independently.
 | 
			
		||||
@@ -1,148 +0,0 @@
 | 
			
		||||
Scriptable Event Handler with State<br/>Main Style
 | 
			
		||||
==================================================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish example
 | 
			
		||||
 | 
			
		||||
A runnable example of this implementation is included.
 | 
			
		||||
 | 
			
		||||
See the [_Examples_]({{rootUrl}}/start/examples/rust.md) section for details.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Initialize Handler Instance with `Engine::call_fn_with_options`
 | 
			
		||||
---------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Use `Engine::call_fn_with_options` instead of `Engine::call_fn` in order to retain new [variables]
 | 
			
		||||
defined inside the custom [`Scope`] when running the `init` function.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
impl Handler {
 | 
			
		||||
    // Create a new 'Handler'.
 | 
			
		||||
    pub fn new(path: impl Into<PathBuf>) -> Self {
 | 
			
		||||
        let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
                    :
 | 
			
		||||
            // Code omitted
 | 
			
		||||
                    :
 | 
			
		||||
 | 
			
		||||
        // Run the 'init' function to initialize the state, retaining variables.
 | 
			
		||||
        let options = CallFnOptions::new()
 | 
			
		||||
                        .eval_ast(false)        // do not re-evaluate the AST
 | 
			
		||||
                        .rewind_scope(false);   // do not rewind scope
 | 
			
		||||
 | 
			
		||||
        // In a real application you'd again be handling errors...
 | 
			
		||||
        engine.call_fn_with_options(options, &mut scope, &ast, "init", ()).unwrap();
 | 
			
		||||
 | 
			
		||||
                    :
 | 
			
		||||
            // Code omitted
 | 
			
		||||
                    :
 | 
			
		||||
 | 
			
		||||
        Self { engine, scope, ast }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Handler Scripting Style
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
Because the stored state is kept in a custom [`Scope`], it is possible for all [functions] defined
 | 
			
		||||
in the handler script to access and modify these state variables.
 | 
			
		||||
 | 
			
		||||
The API registered with the [`Engine`] can be also used throughout the script.
 | 
			
		||||
 | 
			
		||||
### Sample script
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
/// Initialize user-provided state (shadows system-provided state, if any).
 | 
			
		||||
/// Because 'CallFnOptions::rewind_scope' is 'false', new variables introduced
 | 
			
		||||
/// will remain inside the custom 'Scope'.
 | 
			
		||||
fn init() {
 | 
			
		||||
    // Add 'bool_state' and 'obj_state' as new state variables
 | 
			
		||||
    let bool_state = false;
 | 
			
		||||
    let obj_state = new_state(0);
 | 
			
		||||
 | 
			
		||||
    // Constants can also be added!
 | 
			
		||||
    const EXTRA_CONSTANT = "hello, world!";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Without 'OOP' support, the can only be a function.
 | 
			
		||||
fn log(value, data) {
 | 
			
		||||
    print(`State = ${value}, data = ${data}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'start' event handler
 | 
			
		||||
fn start(data) {
 | 
			
		||||
    if bool_state {
 | 
			
		||||
        throw "Already started!";
 | 
			
		||||
    }
 | 
			
		||||
    if obj_state.func1() || obj_state.func2() {
 | 
			
		||||
        throw "Conditions not yet ready to start!";
 | 
			
		||||
    }
 | 
			
		||||
    bool_state = true;
 | 
			
		||||
    obj_state.value = data;
 | 
			
		||||
 | 
			
		||||
    // Constants 'MY_CONSTANT' and 'EXTRA_CONSTANT'
 | 
			
		||||
    // in custom scope are also visible!
 | 
			
		||||
    print(`MY_CONSTANT = ${MY_CONSTANT}`);
 | 
			
		||||
    print(`EXTRA_CONSTANT = ${EXTRA_CONSTANT}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'end' event handler
 | 
			
		||||
fn end(data) {
 | 
			
		||||
    if !bool_state {
 | 
			
		||||
        throw "Not yet started!";
 | 
			
		||||
    }
 | 
			
		||||
    if !obj_state.func1() && !obj_state.func2() {
 | 
			
		||||
        throw "Conditions not yet ready to end!";
 | 
			
		||||
    }
 | 
			
		||||
    bool_state = false;
 | 
			
		||||
    obj_state.value = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'update' event handler
 | 
			
		||||
fn update(data) {
 | 
			
		||||
    obj_state.value += process(data);
 | 
			
		||||
 | 
			
		||||
    // Without OOP support, can only call function
 | 
			
		||||
    log(obj_state.value, data);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Disadvantages of This Style
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
This style is simple to implement and intuitive to use, but it is not very flexible.
 | 
			
		||||
 | 
			
		||||
New user state [variables] are introduced by evaluating a special initialization script [function]
 | 
			
		||||
(e.g. `init`) that defines them, and `Engine::call_fn_with_scope` is used to keep them inside the
 | 
			
		||||
custom [`Scope`] (together with setting `CallFnOptions::rewind_scope` to `false`).
 | 
			
		||||
 | 
			
		||||
However, this has the disadvantage that no other [function] can introduce new state [variables],
 | 
			
		||||
otherwise they'd simply _[shadow]_ existing [variables] in the custom [`Scope`].  Thus, [functions]
 | 
			
		||||
are called during events via `Engine::call_fn` which does not retain any [variables].
 | 
			
		||||
 | 
			
		||||
When there are a large number of state [variables], this style also makes it easy for local
 | 
			
		||||
[variables] defined in user [functions] to accidentally _[shadow]_ a state [variable] with a
 | 
			
		||||
[variable] that just happens to be the same name.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// 'start' event handler
 | 
			
		||||
fn start(data) {
 | 
			
		||||
    let bool_state = false; // <- oops! bad variable name!
 | 
			
		||||
        
 | 
			
		||||
        :                   // there is now no way to access the
 | 
			
		||||
        :                   // state variable 'bool_state'...
 | 
			
		||||
 | 
			
		||||
    if bool_state {         // <- 'bool_state' is not the right thing
 | 
			
		||||
        ...                 //    unless this is what you actually want
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        :
 | 
			
		||||
        :
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,190 +0,0 @@
 | 
			
		||||
Scriptable Event Handler with State<br/>JS Style
 | 
			
		||||
================================================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish example
 | 
			
		||||
 | 
			
		||||
A runnable example of this implementation is included.
 | 
			
		||||
 | 
			
		||||
See the [_Examples_]({{rootUrl}}/start/examples/rust.md) section for details.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Keep State in Object Map
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
This style allows defining new user state [variables] everywhere by packaging them all inside an
 | 
			
		||||
[object map], which is then exposed via the `this` pointer.
 | 
			
		||||
 | 
			
		||||
Because this scripting style resembles JavaScript, it is so named.
 | 
			
		||||
 | 
			
		||||
State variables can be freely created by _all_ [functions] (not just the `init` [function]).
 | 
			
		||||
 | 
			
		||||
The event handler type needs to hold this [object map] instead of a custom [`Scope`].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Scope, Dynamic, AST};
 | 
			
		||||
 | 
			
		||||
// Event handler
 | 
			
		||||
struct Handler {
 | 
			
		||||
    // Scripting engine
 | 
			
		||||
    pub engine: Engine,
 | 
			
		||||
    // The custom 'Scope' can be used to hold global constants
 | 
			
		||||
    pub scope: Scope<'static>,
 | 
			
		||||
    // Use an object map (as a 'Dynamic') to keep stored state
 | 
			
		||||
    pub states: Dynamic,
 | 
			
		||||
    // Program script
 | 
			
		||||
    pub ast: AST
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Bind Object Map to `this` Pointer
 | 
			
		||||
---------------------------------
 | 
			
		||||
 | 
			
		||||
Initialization can simply be done via binding the [object map] containing global states to the
 | 
			
		||||
`this` pointer.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
impl Handler {
 | 
			
		||||
    // Create a new 'Handler'.
 | 
			
		||||
    pub fn new(path: impl Into<PathBuf>) -> Self {
 | 
			
		||||
        let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
                    :
 | 
			
		||||
            // Code omitted
 | 
			
		||||
                    :
 | 
			
		||||
 | 
			
		||||
        // Use an object map to hold state
 | 
			
		||||
        let mut states = Map::new();
 | 
			
		||||
 | 
			
		||||
        // Default states can be added
 | 
			
		||||
        states.insert("bool_state".into(), Dynamic::FALSE);
 | 
			
		||||
 | 
			
		||||
        // Convert the object map into 'Dynamic'
 | 
			
		||||
        let mut states: Dynamic = states.into();
 | 
			
		||||
 | 
			
		||||
        // Use 'call_fn_with_options' instead of 'call_fn' to bind the 'this' pointer
 | 
			
		||||
        let options = CallFnOptions::new()
 | 
			
		||||
                        .eval_ast(false)                // do not re-evaluate the AST
 | 
			
		||||
                        .rewind_scope(true)             // rewind scope
 | 
			
		||||
                        .bind_this_ptr(&mut states);    // bind the 'this' pointer
 | 
			
		||||
 | 
			
		||||
        // In a real application you'd again be handling errors...
 | 
			
		||||
        engine.call_fn_with_options(options, &mut scope, &ast, "init", ()).unwrap();
 | 
			
		||||
 | 
			
		||||
                    :
 | 
			
		||||
            // Code omitted
 | 
			
		||||
                    :
 | 
			
		||||
 | 
			
		||||
        Self { engine, scope, states, ast }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Bind `this` Pointer During Events Handling
 | 
			
		||||
------------------------------------------
 | 
			
		||||
 | 
			
		||||
Events handling should also use `Engine::call_fn_with_options` to bind the [object map] containing
 | 
			
		||||
global states to the `this` pointer via `CallFnOptions::this_ptr`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
pub fn on_event(&mut self, event_name: &str, event_data: i64) -> Dynamic {
 | 
			
		||||
    let engine = &self.engine;
 | 
			
		||||
    let scope = &mut self.scope;
 | 
			
		||||
    let states = &mut self.states;
 | 
			
		||||
    let ast = &self.ast;
 | 
			
		||||
 | 
			
		||||
    let options = CallFnOptions::new()
 | 
			
		||||
                    .eval_ast(false)                // do not re-evaluate the AST
 | 
			
		||||
                    .rewind_scope(true)             // rewind scope
 | 
			
		||||
                    .bind_this_ptr(&mut states);    // bind the 'this' pointer
 | 
			
		||||
 | 
			
		||||
    match event_name {
 | 
			
		||||
        // In a real application you'd be handling errors...
 | 
			
		||||
        "start" => engine.call_fn_with_options(options, scope, ast, "start", (event_data,)).unwrap(),
 | 
			
		||||
            :
 | 
			
		||||
            :
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Handler Scripting Style
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
```admonish note.side "No shadowing"
 | 
			
		||||
 | 
			
		||||
Notice that `this` can never be [shadowed][shadowing] because it is not a valid [variable] name.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Because the stored state is kept in an [object map], which in turn is bound to `this`, it is
 | 
			
		||||
necessary for [functions] to always access or modify these state [variables] via the `this` pointer.
 | 
			
		||||
 | 
			
		||||
As it is impossible to declare a local [variable] named `this`, there is no risk of accidentally
 | 
			
		||||
_[shadowing]_ a state [variable].
 | 
			
		||||
 | 
			
		||||
Because an [object map] is used to hold state values, it is even possible to add user-defined
 | 
			
		||||
[functions], leveraging the [OOP] support for [object maps].
 | 
			
		||||
 | 
			
		||||
### Sample script
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
/// Initialize user-provided state.
 | 
			
		||||
/// State is stored inside an object map bound to 'this'.
 | 
			
		||||
fn init() {
 | 
			
		||||
    // Can detect system-provided default states!
 | 
			
		||||
    // Add 'bool_state' as new state variable if one does not exist
 | 
			
		||||
    if "bool_state" !in this {
 | 
			
		||||
        this.bool_state = false;
 | 
			
		||||
    }
 | 
			
		||||
    // Add 'obj_state' as new state variable (overwrites any existing)
 | 
			
		||||
    this.obj_state = new_state(0);
 | 
			
		||||
 | 
			
		||||
    // Can also add OOP-style functions!
 | 
			
		||||
    this.log = |x| print(`State = ${this.obj_state.value}, data = ${x}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'start' event handler
 | 
			
		||||
fn start(data) {
 | 
			
		||||
    // Access state variables via 'this'
 | 
			
		||||
    if this.bool_state {
 | 
			
		||||
        throw "Already started!";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // New state variables can be created anywhere
 | 
			
		||||
    this.start_mode = data;
 | 
			
		||||
 | 
			
		||||
    if this.obj_state.func1() || this.obj_state.func2() {
 | 
			
		||||
        throw "Conditions not yet ready to start!";
 | 
			
		||||
    }
 | 
			
		||||
    this.bool_state = true;
 | 
			
		||||
    this.obj_state.value = data;
 | 
			
		||||
 | 
			
		||||
    // Constant 'MY_CONSTANT' in custom scope is also visible!
 | 
			
		||||
    print(`MY_CONSTANT = ${MY_CONSTANT}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'end' event handler
 | 
			
		||||
fn end(data) {
 | 
			
		||||
    if !this.bool_state || !("start_mode" in this) {
 | 
			
		||||
        throw "Not yet started!";
 | 
			
		||||
    }
 | 
			
		||||
    if !this.obj_state.func1() && !this.obj_state.func2() {
 | 
			
		||||
        throw "Conditions not yet ready to end!";
 | 
			
		||||
    }
 | 
			
		||||
    this.bool_state = false;
 | 
			
		||||
    this.obj_state.value = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'update' event handler
 | 
			
		||||
fn update(data) {
 | 
			
		||||
    this.obj_state.value += process(data);
 | 
			
		||||
 | 
			
		||||
    // Call user-defined function OOP-style!
 | 
			
		||||
    this.log(data);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,163 +0,0 @@
 | 
			
		||||
Scriptable Event Handler with State<br/>Map Style
 | 
			
		||||
=================================================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish example
 | 
			
		||||
 | 
			
		||||
A runnable example of this implementation is included.
 | 
			
		||||
 | 
			
		||||
See the [_Examples_]({{rootUrl}}/start/examples/rust.md) section for details.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
I Hate `this`!  How Can I Get Rid of It?
 | 
			
		||||
----------------------------------------
 | 
			
		||||
 | 
			
		||||
You're using Rust and you don't want people to think you're writing lowly JavaScript?
 | 
			
		||||
 | 
			
		||||
Taking inspiration from the [_JS Style_](events-2.md), a slight modification of the
 | 
			
		||||
[_Main Style_](events-1.md) is to store all states inside an [object map] inside a custom [`Scope`].
 | 
			
		||||
 | 
			
		||||
Nevertheless, instead of writing `this.variable_name` everywhere to access a state variable
 | 
			
		||||
(in the [_JS Style_](events-2.md)), you'd write `state.variable_name` instead.
 | 
			
		||||
 | 
			
		||||
It is up to you to decide whether this is an improvement!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Handler Initialization
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
```admonish note.side "No shadowing 'state'"
 | 
			
		||||
 | 
			
		||||
Notice that a [variable definition filter] is used to prevent [shadowing] of the states [object map].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Implementation wise, this style follows closely the [_Main Style_](events-1.md), but a single
 | 
			
		||||
[object map] is added to the custom [`Scope`] which holds all state values.
 | 
			
		||||
 | 
			
		||||
Global [constants] can still be added to the custom [`Scope`] as normal and used through the script.
 | 
			
		||||
 | 
			
		||||
Calls to the `init` [function] no longer need to avoid rewinding the [`Scope`] because state
 | 
			
		||||
[variables] are added as properties under the states [object map].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
impl Handler {
 | 
			
		||||
    // Create a new 'Handler'.
 | 
			
		||||
    pub fn new(path: impl Into<PathBuf>) -> Self {
 | 
			
		||||
        let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
        // Forbid shadowing of 'state' variable
 | 
			
		||||
        engine.on_def_var(|_, info, _| Ok(info.name != "state"));
 | 
			
		||||
 | 
			
		||||
                    :
 | 
			
		||||
            // Code omitted
 | 
			
		||||
                    :
 | 
			
		||||
 | 
			
		||||
        // Use an object map to hold state
 | 
			
		||||
        let mut states = Map::new();
 | 
			
		||||
 | 
			
		||||
        // Default states can be added
 | 
			
		||||
        states.insert("bool_state".into(), Dynamic::FALSE);
 | 
			
		||||
 | 
			
		||||
        // Add the main states-holding object map and call it 'state'
 | 
			
		||||
        scope.push("state", states);
 | 
			
		||||
 | 
			
		||||
        // Just a simple 'call_fn' can do here because we're rewinding the 'Scope'
 | 
			
		||||
        // In a real application you'd again be handling errors...
 | 
			
		||||
        engine.call_fn(&mut scope, &ast, "init", ()).unwrap();
 | 
			
		||||
 | 
			
		||||
                    :
 | 
			
		||||
            // Code omitted
 | 
			
		||||
                    :
 | 
			
		||||
 | 
			
		||||
        Self { engine, scope, ast }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Handler Scripting Style
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
The stored state is kept in an [object map] in the custom [`Scope`].
 | 
			
		||||
 | 
			
		||||
In this example, that [object map] is named `state`, but it can be any name.
 | 
			
		||||
 | 
			
		||||
### User-defined functions in state
 | 
			
		||||
 | 
			
		||||
Because an [object map] is used to hold state values, it is even possible to add user-defined
 | 
			
		||||
[functions], leveraging the [OOP] support for [object maps].
 | 
			
		||||
 | 
			
		||||
However, within these user-defined [functions], the `this` pointer binds to the [object map].
 | 
			
		||||
Therefore, the variable-accessing syntax is different from the main body of the script.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
fn do_action() {
 | 
			
		||||
    // Access state: `state.xxx`
 | 
			
		||||
    state.number = 42;
 | 
			
		||||
 | 
			
		||||
    // Add OOP functions - you still need to use `this`...
 | 
			
		||||
    state.log = |x| print(`State = ${this.value}, data = ${x}`);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Sample script
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
/// Initialize user-provided state.
 | 
			
		||||
/// State is stored inside an object map bound to 'state'.
 | 
			
		||||
fn init() {
 | 
			
		||||
    // Add 'bool_state' as new state variable if one does not exist
 | 
			
		||||
    if "bool_state" !in state {
 | 
			
		||||
        state.bool_state = false;
 | 
			
		||||
    }
 | 
			
		||||
    // Add 'obj_state' as new state variable (overwrites any existing)
 | 
			
		||||
    state.obj_state = new_state(0);
 | 
			
		||||
 | 
			
		||||
    // Can also add OOP-style functions!
 | 
			
		||||
    state.log = |x| print(`State = ${this.obj_state.value}, data = ${x}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'start' event handler
 | 
			
		||||
fn start(data) {
 | 
			
		||||
    // Can detect system-provided default states!
 | 
			
		||||
    // Access state variables in 'state'
 | 
			
		||||
    if state.bool_state {
 | 
			
		||||
        throw "Already started!";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // New values can be added to the state
 | 
			
		||||
    state.start_mode = data;
 | 
			
		||||
 | 
			
		||||
    if state.obj_state.func1() || state.obj_state.func2() {
 | 
			
		||||
        throw "Conditions not yet ready to start!";
 | 
			
		||||
    }
 | 
			
		||||
    state.bool_state = true;
 | 
			
		||||
    state.obj_state.value = data;
 | 
			
		||||
 | 
			
		||||
    // Constant 'MY_CONSTANT' in custom scope is also visible!
 | 
			
		||||
    print(`MY_CONSTANT = ${MY_CONSTANT}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'end' event handler
 | 
			
		||||
fn end(data) {
 | 
			
		||||
    if !state.bool_state || "start_mode" !in state {
 | 
			
		||||
        throw "Not yet started!";
 | 
			
		||||
    }
 | 
			
		||||
    if !state.obj_state.func1() && !state.obj_state.func2() {
 | 
			
		||||
        throw "Conditions not yet ready to end!";
 | 
			
		||||
    }
 | 
			
		||||
    state.bool_state = false;
 | 
			
		||||
    state.obj_state.value = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// 'update' event handler
 | 
			
		||||
fn update(data) {
 | 
			
		||||
    state.obj_state.value += process(data);
 | 
			
		||||
 | 
			
		||||
    // Call user-defined function OOP-style!
 | 
			
		||||
    state.log(data);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,232 +0,0 @@
 | 
			
		||||
Scriptable Event Handler with State
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish tip "IMPORTANT PATTERN"
 | 
			
		||||
 | 
			
		||||
In many usage scenarios, a scripting engine is used to provide flexibility in event handling.
 | 
			
		||||
 | 
			
		||||
That means to execute certain **actions** in response to certain **_events_** that occur at run-time,
 | 
			
		||||
and scripts are used to provide flexibility for coding those actions.
 | 
			
		||||
 | 
			
		||||
You'd be surprised how many applications fit under this pattern – they are all essentially
 | 
			
		||||
event handling systems.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish example "Examples"
 | 
			
		||||
 | 
			
		||||
Because of the importance of this pattern, runnable examples are included.
 | 
			
		||||
 | 
			
		||||
See the [_Examples_](../start/examples/rust.md) section for details.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system sends _events_ that must be handled.
 | 
			
		||||
 | 
			
		||||
* Flexibility in event handling must be provided, through user-side scripting.
 | 
			
		||||
 | 
			
		||||
* State must be kept between invocations of event handlers.
 | 
			
		||||
 | 
			
		||||
* State may be provided by the system or the user, or both.
 | 
			
		||||
 | 
			
		||||
* Default implementations of event handlers can be provided.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* An _event handler_ object is declared that holds the following items:
 | 
			
		||||
  * [`Engine`] with registered functions serving as an API,
 | 
			
		||||
  * [`AST`] of the user script,
 | 
			
		||||
  * [`Scope`] containing system-provided default state.
 | 
			
		||||
 | 
			
		||||
* User-provided state is initialized by a [function] called via [`Engine::call_fn_with_options`][`call_fn`].
 | 
			
		||||
 | 
			
		||||
* Upon an event, the appropriate event handler [function] in the script is called via
 | 
			
		||||
  [`Engine::call_fn`][`call_fn`].
 | 
			
		||||
 | 
			
		||||
* Optionally, trap the `EvalAltResult::ErrorFunctionNotFound` error to provide a default implementation.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Basic Infrastructure
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
### Declare handler object
 | 
			
		||||
 | 
			
		||||
In most cases, it would be simpler to store an [`Engine`] instance together with the handler object
 | 
			
		||||
because it only requires registering all API functions only once.
 | 
			
		||||
 | 
			
		||||
In rare cases where handlers are created and destroyed in a tight loop, a new [`Engine`] instance
 | 
			
		||||
can be created for each event. See [_One Engine Instance Per Call_](parallel.md) for more details.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Scope, AST};
 | 
			
		||||
 | 
			
		||||
// Event handler
 | 
			
		||||
struct Handler {
 | 
			
		||||
    // Scripting engine
 | 
			
		||||
    pub engine: Engine,
 | 
			
		||||
    // Use a custom 'Scope' to keep stored state
 | 
			
		||||
    pub scope: Scope<'static>,
 | 
			
		||||
    // Program script
 | 
			
		||||
    pub ast: AST
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Register API for custom types
 | 
			
		||||
 | 
			
		||||
[Custom types] are often used to hold state. The easiest way to register an entire API is via a [plugin module].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::plugin::*;
 | 
			
		||||
 | 
			
		||||
// A custom type to a hold state value.
 | 
			
		||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
 | 
			
		||||
pub struct TestStruct {
 | 
			
		||||
    data: i64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Plugin module containing API to TestStruct
 | 
			
		||||
#[export_module]
 | 
			
		||||
mod test_struct_api {
 | 
			
		||||
    #[rhai_fn(global)]
 | 
			
		||||
    pub fn new_state(value: i64) -> TestStruct {
 | 
			
		||||
        TestStruct { data: value }
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(global)]
 | 
			
		||||
    pub fn func1(obj: &mut TestStruct) -> bool {
 | 
			
		||||
                :
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(global)]
 | 
			
		||||
    pub fn func2(obj: &mut TestStruct) -> i64 {
 | 
			
		||||
                :
 | 
			
		||||
    }
 | 
			
		||||
    pub fn process(data: i64) -> i64 {
 | 
			
		||||
                :
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "value", pure)]
 | 
			
		||||
    pub fn get_value(obj: &mut TestStruct) -> i64 {
 | 
			
		||||
        obj.data
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(set = "value")]
 | 
			
		||||
    pub fn set_value(obj: &mut TestStruct, value: i64) {
 | 
			
		||||
        obj.data = value;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Initialize handler object
 | 
			
		||||
 | 
			
		||||
Steps to initialize the event handler:
 | 
			
		||||
 | 
			
		||||
1. Register an API with the [`Engine`],
 | 
			
		||||
2. Create a custom [`Scope`] to serve as the stored state,
 | 
			
		||||
3. Add default state [variables] into the custom [`Scope`],
 | 
			
		||||
4. Optionally, call an initiation [function] to create new state [variables];
 | 
			
		||||
   `Engine::call_fn_with_options` is used instead of `Engine::call_fn` so that [variables] created
 | 
			
		||||
   inside the [function] will not be removed from the custom [`Scope`] upon exit,
 | 
			
		||||
5. Get the handler script and [compile][`AST`] it,
 | 
			
		||||
6. Store the compiled [`AST`] for future evaluations,
 | 
			
		||||
7. Run the [`AST`] to initialize event handler state [variables].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
impl Handler {
 | 
			
		||||
    // Create a new 'Handler'.
 | 
			
		||||
    pub fn new(path: impl Into<PathBuf>) -> Self {
 | 
			
		||||
        let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
        // Register custom types and APIs
 | 
			
		||||
        engine.register_type_with_name::<TestStruct>("TestStruct")
 | 
			
		||||
              .register_global_module(exported_module!(test_struct_api).into());
 | 
			
		||||
 | 
			
		||||
        // Create a custom 'Scope' to hold state
 | 
			
		||||
        let mut scope = Scope::new();
 | 
			
		||||
 | 
			
		||||
        // Add any system-provided state into the custom 'Scope'.
 | 
			
		||||
        // Constants can be used to optimize the script.
 | 
			
		||||
        scope.push_constant("MY_CONSTANT", 42_i64);
 | 
			
		||||
 | 
			
		||||
                    :
 | 
			
		||||
                    :
 | 
			
		||||
        // Initialize state variables
 | 
			
		||||
                    :
 | 
			
		||||
                    :
 | 
			
		||||
 | 
			
		||||
        // Compile the handler script.
 | 
			
		||||
        // In a real application you'd be handling errors...
 | 
			
		||||
        let ast = engine.compile_file_with_scope(&mut scope, path).unwrap();
 | 
			
		||||
 | 
			
		||||
        // The event handler is essentially these three items:
 | 
			
		||||
        Self { engine, scope, ast }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Hook up events
 | 
			
		||||
 | 
			
		||||
There is usually an interface or trait that gets called when an event comes from the system.
 | 
			
		||||
 | 
			
		||||
Mapping an event from the system into a scripted handler is straight-forward, via `Engine::call_fn`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
impl Handler {
 | 
			
		||||
    // Say there are three events: 'start', 'end', 'update'.
 | 
			
		||||
    // In a real application you'd be handling errors...
 | 
			
		||||
    pub fn on_event(&mut self, event_name: &str, event_data: i64) -> Dynamic {
 | 
			
		||||
        let engine = &self.engine;
 | 
			
		||||
        let scope = &mut self.scope;
 | 
			
		||||
        let ast = &self.ast;
 | 
			
		||||
 | 
			
		||||
        match event_name {
 | 
			
		||||
            // The 'start' event maps to function 'start'.
 | 
			
		||||
            // In a real application you'd be handling errors...
 | 
			
		||||
            "start" => engine.call_fn(scope, ast, "start", (event_data,)).unwrap(),
 | 
			
		||||
 | 
			
		||||
            // The 'end' event maps to function 'end'.
 | 
			
		||||
            // In a real application you'd be handling errors...
 | 
			
		||||
            "end" => engine.call_fn(scope, ast, "end", (event_data,)).unwrap(),
 | 
			
		||||
 | 
			
		||||
            // The 'update' event maps to function 'update'.
 | 
			
		||||
            // This event provides a default implementation when the scripted function is not found.
 | 
			
		||||
            "update" =>
 | 
			
		||||
                engine.call_fn(scope, ast, "update", (event_data,))
 | 
			
		||||
                      .or_else(|err| match *err {
 | 
			
		||||
                          EvalAltResult::ErrorFunctionNotFound(fn_name, _)
 | 
			
		||||
                              if fn_name.starts_with("update") =>
 | 
			
		||||
                          {
 | 
			
		||||
                              // Default implementation of 'update' event handler.
 | 
			
		||||
                              self.scope.set_value("obj_state", TestStruct::new(42));
 | 
			
		||||
                              // Turn function-not-found into a success.
 | 
			
		||||
                              Ok(Dynamic::UNIT)
 | 
			
		||||
                          }
 | 
			
		||||
                          _ => Err(err)
 | 
			
		||||
                      }).unwrap(),
 | 
			
		||||
 | 
			
		||||
            // In a real application you'd be handling unknown events...
 | 
			
		||||
            _ => panic!("unknown event: {}", event_name)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Scripting Styles
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
Depending on needs and scripting style, there are three different ways to implement this pattern.
 | 
			
		||||
 | 
			
		||||
|                                                   |                                          [Main style](events-1.md)                                          |                               [JS style](events-2.md)                               |                           [Map style](events-3.md)                           |
 | 
			
		||||
| ------------------------------------------------- | :---------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------: | :--------------------------------------------------------------------------: |
 | 
			
		||||
| States store                                      |                                              custom [`Scope`]                                               |                            [object map] bound to `this`                             |                       [object map] in custom [`Scope`]                       |
 | 
			
		||||
| Access state [variable]                           |                                              normal [variable]                                              |                          [property][object map] of `this`                           |                   [property][object map] of state variable                   |
 | 
			
		||||
| Access global [constants]?                        |                                                     yes                                                     |                                         yes                                         |                                     yes                                      |
 | 
			
		||||
| Add new state [variable]?                         |                                           `init` [function] only                                            |                                   all [functions]                                   |                               all [functions]                                |
 | 
			
		||||
| Add new global [constants]?                       |                                                     yes                                                     |                                       **no**                                        |                                    **no**                                    |
 | 
			
		||||
| [OOP]-style [functions] on states?                |                                                   **no**                                                    |                                         yes                                         |                                     yes                                      |
 | 
			
		||||
| Detect system-provided initial states?            |                                                   **no**                                                    |                                         yes                                         |                                     yes                                      |
 | 
			
		||||
| Local [variable] may _[shadow]_ state [variable]? |                                                     yes                                                     |                                       **no**                                        |                                    **no**                                    |
 | 
			
		||||
| Benefits                                          |                                                   simple                                                    |                                   fewer surprises                                   |                                  versatile                                   |
 | 
			
		||||
| Disadvantages                                     | <ul><li>no new variables in [functions] (apart from `init`)</li><li>easy variable name collisions</li></ul> | <ul><li>`this.xxx` all over the place</li><li>more complex implementation</li></ul> | <ul><li>`state.xxx` all over the place</li><li>inconsistent syntax</li></ul> |
 | 
			
		||||
@@ -1,155 +0,0 @@
 | 
			
		||||
Mutable Global State
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Don't Do It™
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
```admonish question.side "Consider JavaScript"
 | 
			
		||||
 | 
			
		||||
Generations of programmers struggled to get around mutable global state (a.k.a. the `window` object)
 | 
			
		||||
in the design of JavaScript.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In contrast to [global constants](constants.md), _mutable_ global states are **strongly
 | 
			
		||||
discouraged** because:
 | 
			
		||||
 | 
			
		||||
1) It is a sure-fire way to create race conditions – that is why Rust does not support it;
 | 
			
		||||
 | 
			
		||||
2) It adds considerably to debug complexity – it is difficult to reason, in large code bases,
 | 
			
		||||
   where/when a state value is being modified;
 | 
			
		||||
 | 
			
		||||
3) It forces hard (but obscure) dependencies between separate pieces of code that are difficult to
 | 
			
		||||
   break when the need arises;
 | 
			
		||||
 | 
			
		||||
4) It is almost impossible to add new layers of redirection and/or abstraction afterwards without
 | 
			
		||||
   major surgery.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Alternative – Use `this`
 | 
			
		||||
------------------------------
 | 
			
		||||
 | 
			
		||||
In the majority of the such scenarios, there is only _one_ mutable global state of interest.
 | 
			
		||||
 | 
			
		||||
Therefore, it is a _much_ better solution to bind that global state to the `this` pointer.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Say this is a mutable global state...
 | 
			
		||||
let state = #{ counter: 0 };
 | 
			
		||||
 | 
			
		||||
// This function tries to access the global 'state'
 | 
			
		||||
// which will fail.
 | 
			
		||||
fn inc() {
 | 
			
		||||
    state.counter += 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The function should be written with 'this'
 | 
			
		||||
fn inc() {
 | 
			
		||||
    this.counter += 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
state.inc();        // call 'inc' with 'state' bound to 'this'
 | 
			
		||||
 | 
			
		||||
// Or this way... why hard-code the state in the first place?
 | 
			
		||||
fn inc() {
 | 
			
		||||
    this += 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
state.counter.inc();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish question.small "Why is this better?"
 | 
			
		||||
 | 
			
		||||
There are good reasons why using `this` is a better solution:
 | 
			
		||||
 | 
			
		||||
* the state is never _hidden_ – it is always clear to see what is being modified
 | 
			
		||||
* it is just as fast – the `this` pointer works by reference
 | 
			
		||||
* you can pass other states in, in the future, without changing the script code
 | 
			
		||||
* there are no hard links within functions that will be difficult to unravel
 | 
			
		||||
* only the [variable] bound to `this` is ever modified; everything else is immutable
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish danger.small "I don't care! I want it! Just tell me how to do it! Now!"
 | 
			
		||||
 | 
			
		||||
This is not something that Rhai encourages.  _You Have Been Warned™_.
 | 
			
		||||
 | 
			
		||||
There are two ways...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Option 1 – Get/Set Functions
 | 
			
		||||
----------------------------------
 | 
			
		||||
 | 
			
		||||
This is similar to the [Control Layer](control.md) pattern.
 | 
			
		||||
 | 
			
		||||
Use get/set functions to read/write the global mutable state.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// The globally mutable shared value
 | 
			
		||||
let value = Rc::new(RefCell::new(42));
 | 
			
		||||
 | 
			
		||||
// Register an API to access the globally mutable shared value
 | 
			
		||||
let v = value.clone();
 | 
			
		||||
engine.register_fn("get_global_value", move || *v.borrow());
 | 
			
		||||
 | 
			
		||||
let v = value.clone();
 | 
			
		||||
engine.register_fn("set_global_value", move |value: i64| *v.borrow_mut() = value);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
These functions can be used in script [functions] to access the shared global state.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn foo() {
 | 
			
		||||
    let current = get_global_value();       // Get global state value
 | 
			
		||||
    current += 1;
 | 
			
		||||
    set_global_value(current);              // Modify global state value
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This option is preferred because it is possible to modify the get/set functions later on to
 | 
			
		||||
add/change functionalities without introducing breaking script changes.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Option 2 – Variable Resolver
 | 
			
		||||
----------------------------------
 | 
			
		||||
 | 
			
		||||
Declare a [variable resolver] that returns a _shared_ value which is the global state.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Use a shared value as the global state
 | 
			
		||||
let value: Dynamic = 1.into();
 | 
			
		||||
let mut value = value.into_shared();        // convert into shared value
 | 
			
		||||
 | 
			
		||||
// Clone the shared value
 | 
			
		||||
let v = value.clone();
 | 
			
		||||
 | 
			
		||||
// Register a variable resolver.
 | 
			
		||||
engine.on_var(move |name, _, _| {
 | 
			
		||||
    match name
 | 
			
		||||
        "value" => Ok(Some(v.clone())),
 | 
			
		||||
        _ => Ok(None)
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// The shared global state can be modified
 | 
			
		||||
*value.write_lock::<i64>().unwrap() = 42;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The global state variable can now be used just like a normal local variable,
 | 
			
		||||
including modifications.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn foo() {
 | 
			
		||||
    value = value * 2;
 | 
			
		||||
//          ^ global variable can be read
 | 
			
		||||
//  ^ global variable can also be modified
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish danger.small "Anti-Pattern"
 | 
			
		||||
 | 
			
		||||
This option makes mutable global state so easy to implement that it should actually be
 | 
			
		||||
considered an _Anti-Pattern_.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,107 +0,0 @@
 | 
			
		||||
Hot Reloading
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system where scripts are used for behavioral control.
 | 
			
		||||
 | 
			
		||||
* All or parts of the control scripts need to be modified dynamically without re-initializing the
 | 
			
		||||
  host system.
 | 
			
		||||
 | 
			
		||||
* New scripts must be active as soon as possible after modifications are detected.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* The Rhai [`Engine`] is _re-entrant_, meaning that it is decoupled from scripts.
 | 
			
		||||
 | 
			
		||||
* A new script only needs to be recompiled and the new [`AST`] replaces the old for new behaviors
 | 
			
		||||
  to be active.
 | 
			
		||||
 | 
			
		||||
* Surgically _patch_ scripts when only parts of the scripts are modified.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
### Embed scripting engine and script into system
 | 
			
		||||
 | 
			
		||||
Say, a system has a Rhai [`Engine`] plus a compiled script (in [`AST`] form), with the [`AST`] kept
 | 
			
		||||
with interior mutability...
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Main system object
 | 
			
		||||
struct System {
 | 
			
		||||
    engine: Engine,
 | 
			
		||||
    script: Rc<RefCell<AST>>,
 | 
			
		||||
      :
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Embed Rhai 'Engine' and control script
 | 
			
		||||
let engine = Engine::new();
 | 
			
		||||
let ast = engine.compile_file("config.rhai")?;
 | 
			
		||||
 | 
			
		||||
let mut system = System { engine, script: Rc::new(RefCell::new(ast)) };
 | 
			
		||||
 | 
			
		||||
// Handle events with script functions
 | 
			
		||||
system.on_event(|sys: &System, event: &str, data: Map| {
 | 
			
		||||
    let mut scope = Scope::new();
 | 
			
		||||
 | 
			
		||||
    // Call script function which is the same name as the event
 | 
			
		||||
    sys.engine.call_fn(&mut scope, sys.script.borrow(), event, (data,)).unwrap();
 | 
			
		||||
 | 
			
		||||
    result
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Hot reload entire script upon change
 | 
			
		||||
 | 
			
		||||
If the control scripts are small enough and changes are infrequent, it is much simpler just to
 | 
			
		||||
recompile the whole set of script and replace the original [`AST`] with the new one.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Watch for script file change
 | 
			
		||||
system.watch(|sys: &System, file: &str| {
 | 
			
		||||
    // Compile the new script
 | 
			
		||||
    let ast = sys.engine.compile_file(file.into())?;
 | 
			
		||||
 | 
			
		||||
    // Hot reload - just replace the old script!
 | 
			
		||||
    *sys.script.borrow_mut() = ast;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Hot patch specific functions
 | 
			
		||||
 | 
			
		||||
If the control scripts are large and complicated, and if the system can detect changes to specific [functions],
 | 
			
		||||
it is also possible to _patch_ just the changed [functions].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Watch for changes in the script
 | 
			
		||||
system.watch_for_script_change(|sys: &mut System, fn_name: &str| {
 | 
			
		||||
    // Get the script file that contains the function
 | 
			
		||||
    let script = get_script_file_path(fn_name);
 | 
			
		||||
 | 
			
		||||
    // Compile the new script
 | 
			
		||||
    let mut patch_ast = sys.engine.compile_file(script)?;
 | 
			
		||||
 | 
			
		||||
    // Remove everything other than the specified function
 | 
			
		||||
    patch_ast.clear_statements();
 | 
			
		||||
    patch_ast.retain_functions(|_, _, name, _| name == fn_name);
 | 
			
		||||
 | 
			
		||||
    // Hot reload (via +=) only those functions in the script!
 | 
			
		||||
    *sys.script.borrow_mut() += patch_ast;
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Multi-threaded considerations"
 | 
			
		||||
 | 
			
		||||
For a multi-threaded environments, replace `Rc` with `Arc`, `RefCell` with `RwLock` or `Mutex`, and
 | 
			
		||||
turn on the [`sync`] feature.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,119 +0,0 @@
 | 
			
		||||
Simulate Macros to Simplify Scripts
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* Scripts need to access existing data in [variables].
 | 
			
		||||
 | 
			
		||||
* The particular fields to access correspond to long/complex expressions (e.g. long
 | 
			
		||||
  [indexing][indexers] and/or [property][getters/setters] chains `foo[x][y].bar[z].baz`).
 | 
			
		||||
 | 
			
		||||
* Usage is prevalent inside the scripts, requiring extensive duplications of code that
 | 
			
		||||
  are prone to typos and errors.
 | 
			
		||||
 | 
			
		||||
* There are a few such [variables] to modify at the same time – otherwise, it would
 | 
			
		||||
  be simpler to bind the `this` pointer to the [variable].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Pick a _macro_ syntax that is unlikely to conflict with content in literal [strings].
 | 
			
		||||
 | 
			
		||||
* Before script evaluation/compilation, globally replace macros with their corresponding expansions.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Pick a Macro Syntax
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
```admonish danger.side "Warning: Not real macros"
 | 
			
		||||
 | 
			
		||||
The technique described here is to _simulate_ macros.
 | 
			
		||||
They are not _REAL_ macros.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Pick a syntax that is intuitive for the domain but **unlikely to occur naturally inside
 | 
			
		||||
[string] literals**.
 | 
			
		||||
 | 
			
		||||
| Sample Syntax | Sample usage       |
 | 
			
		||||
| ------------- | ------------------ |
 | 
			
		||||
| `#FOO`        | `#FOO = 42;`       |
 | 
			
		||||
| `$Bar`        | `$Bar.work();`     |
 | 
			
		||||
| `<Baz>`       | `print(<Baz>);`    |
 | 
			
		||||
| `#HELLO#`     | `let x = #HELLO#;` |
 | 
			
		||||
| `%HEY%`       | `%HEY% += 1;`      |
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.small "Tip: Avoid normal words"
 | 
			
		||||
 | 
			
		||||
Avoid normal syntax that may show up inside a [string] literal.
 | 
			
		||||
 | 
			
		||||
For example, if using `Target` as a macro:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// This script...
 | 
			
		||||
Target.do_damage(10);
 | 
			
		||||
 | 
			
		||||
if Target.hp <= 0 {
 | 
			
		||||
    print("Target is destroyed!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Will turn to this...
 | 
			
		||||
entities["monster"].do_damage(10);
 | 
			
		||||
 | 
			
		||||
if entities["monster"].hp <= 0 {
 | 
			
		||||
    // Text in string literal erroneously replaced!
 | 
			
		||||
    print("entities["monster"] is destroyed!");
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Global Search/Replace
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Replace macros with expansions
 | 
			
		||||
let script = script.replace("#FOO", "foo[x][y].bar[z].baz");
 | 
			
		||||
 | 
			
		||||
let mut scope = Scope::new();
 | 
			
		||||
 | 
			
		||||
// Add global variables
 | 
			
		||||
scope.push("foo", ...);
 | 
			
		||||
scope.push_constant("x", ...);
 | 
			
		||||
scope.push_constant("y", ...);
 | 
			
		||||
scope.push_constant("z", ...);
 | 
			
		||||
 | 
			
		||||
// Run the script as normal
 | 
			
		||||
engine.run_with_scope(&mut scope, script)?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish example.small "Example script"
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
print(`Found entity FOO at (${x},${y},${z})`);
 | 
			
		||||
 | 
			
		||||
let speed = #FOO.speed;
 | 
			
		||||
 | 
			
		||||
if speed < 42 {
 | 
			
		||||
    #FOO.speed *= 2;
 | 
			
		||||
} else {
 | 
			
		||||
    #FOO.teleport(#FOO.home());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print(`FOO is now at (${ #FOO.current_location() })`);
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
```admonish bug.small "Character positions"
 | 
			
		||||
 | 
			
		||||
After macro expansion, the _character positions_ of different script
 | 
			
		||||
elements will be shifted based on the length of the expanded text.
 | 
			
		||||
 | 
			
		||||
Therefore, error positions may no longer point to the correct locations
 | 
			
		||||
in the original, unexpanded scripts.
 | 
			
		||||
 | 
			
		||||
Line numbers are not affected.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,136 +0,0 @@
 | 
			
		||||
Multi-Layered Functions
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system is divided into separate _layers_, each providing logic in terms of scripted [functions].
 | 
			
		||||
 | 
			
		||||
* A lower layer provides _default implementations_ of certain [functions].
 | 
			
		||||
 | 
			
		||||
* Higher layers each provide progressively more specific implementations of the same [functions].
 | 
			
		||||
 | 
			
		||||
* A more specific [function], if defined in a higher layer, always overrides the implementation
 | 
			
		||||
  in a lower layer.
 | 
			
		||||
 | 
			
		||||
* This is akin to object-oriented programming but with [functions].
 | 
			
		||||
 | 
			
		||||
* This type of system is extremely convenient for dynamic business rules configuration, setting
 | 
			
		||||
  corporate-wide policies, granting permissions for specific roles etc. where specific, local rules
 | 
			
		||||
  need to override corporate-wide defaults.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip "Practical scenario"
 | 
			
		||||
 | 
			
		||||
Assuming a LOB (line-of-business) system for a large MNC (multi-national corporation) with branches,
 | 
			
		||||
facilities and offices across the globe.
 | 
			
		||||
 | 
			
		||||
The system needs to provide basic, corporate-wide policies to be enforced through the worldwide
 | 
			
		||||
organization, but also cater for country- or region-specific rules, practices and regulations.
 | 
			
		||||
 | 
			
		||||
|    Layer    | Description                                         |
 | 
			
		||||
| :---------: | --------------------------------------------------- |
 | 
			
		||||
| `corporate` | corporate-wide policies                             |
 | 
			
		||||
| `regional`  | regional policy overrides                           |
 | 
			
		||||
|  `country`  | country-specific modifications for legal compliance |
 | 
			
		||||
|  `office`   | special treatments for individual office locations  |
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Each layer is a separate script.
 | 
			
		||||
 | 
			
		||||
* The lowest layer script is compiled into a base [`AST`].
 | 
			
		||||
 | 
			
		||||
* Higher layer scripts are also compiled into [`AST`] and _combined_ into the base using
 | 
			
		||||
  [`AST::combine`]({{rootUrl}}/engine/ast.md) (or the `+=` operator), overriding any existing [functions].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
Assume the following four scripts, one for each layer:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
┌────────────────┐
 | 
			
		||||
│ corporate.rhai │
 | 
			
		||||
└────────────────┘
 | 
			
		||||
 | 
			
		||||
// Default implementation of 'foo'.
 | 
			
		||||
fn foo(x) { x + 1 }
 | 
			
		||||
 | 
			
		||||
// Default implementation of 'bar'.
 | 
			
		||||
fn bar(x, y) { x + y }
 | 
			
		||||
 | 
			
		||||
// Default implementation of 'no_touch'.
 | 
			
		||||
fn no_touch() { throw "do not touch me!"; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌───────────────┐
 | 
			
		||||
│ regional.rhai │
 | 
			
		||||
└───────────────┘
 | 
			
		||||
 | 
			
		||||
// Specific implementation of 'foo'.
 | 
			
		||||
fn foo(x) { x * 2 }
 | 
			
		||||
 | 
			
		||||
// New implementation for this layer.
 | 
			
		||||
fn baz() { print("hello!"); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌──────────────┐
 | 
			
		||||
│ country.rhai │
 | 
			
		||||
└──────────────┘
 | 
			
		||||
 | 
			
		||||
// Specific implementation of 'bar'.
 | 
			
		||||
fn bar(x, y) { x - y }
 | 
			
		||||
 | 
			
		||||
// Specific implementation of 'baz'.
 | 
			
		||||
fn baz() { print("hey!"); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌─────────────┐
 | 
			
		||||
│ office.rhai │
 | 
			
		||||
└─────────────┘
 | 
			
		||||
 | 
			
		||||
// Specific implementation of 'foo'.
 | 
			
		||||
fn foo(x) { x + 42 }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Load and combine them sequentially:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Compile the baseline layer.
 | 
			
		||||
let mut ast = engine.compile_file("corporate.rhai".into())?;
 | 
			
		||||
 | 
			
		||||
// Combine the first layer.
 | 
			
		||||
let lowest = engine.compile_file("regional.rhai".into())?;
 | 
			
		||||
ast += lowest;
 | 
			
		||||
 | 
			
		||||
// Combine the second layer.
 | 
			
		||||
let middle = engine.compile_file("country.rhai".into())?;
 | 
			
		||||
ast += middle;
 | 
			
		||||
 | 
			
		||||
// Combine the third layer.
 | 
			
		||||
let highest = engine.compile_file("office.rhai".into())?;
 | 
			
		||||
ast += highest;
 | 
			
		||||
 | 
			
		||||
// Now, 'ast' contains the following functions:
 | 
			
		||||
//
 | 
			
		||||
// fn no_touch() {                // from 'corporate.rhai'
 | 
			
		||||
//     throw "do not touch me!";
 | 
			
		||||
// }
 | 
			
		||||
// fn foo(x) { x + 42 }           // from 'office.rhai'
 | 
			
		||||
// fn bar(x, y) { x - y }         // from 'country.rhai'
 | 
			
		||||
// fn baz() { print("hey!"); }    // from 'country.rhai'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish failure.small "No super call"
 | 
			
		||||
 | 
			
		||||
Unfortunately, there is no `super` call that calls the base implementation (i.e. no way for a
 | 
			
		||||
higher-layer function to call an equivalent lower-layer function).
 | 
			
		||||
```
 | 
			
		||||
@@ -1,116 +0,0 @@
 | 
			
		||||
Multi-Threaded Synchronization
 | 
			
		||||
==============================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenarios"
 | 
			
		||||
 | 
			
		||||
* A system needs to communicate with an [`Engine`] running in a separate thread.
 | 
			
		||||
 | 
			
		||||
* Multiple [`Engine`]s running in separate threads need to coordinate/synchronize with each other.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* An MPSC channel (or any other appropriate synchronization primitive) is used to send/receive
 | 
			
		||||
  messages to/from an [`Engine`] running in a separate thread.
 | 
			
		||||
 | 
			
		||||
* An API is registered with the [`Engine`] that is essentially _blocking_ until synchronization is achieved.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine};
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    // Channel: Script -> Master
 | 
			
		||||
    let (tx_script, rx_master) = std::sync::mpsc::channel();
 | 
			
		||||
    // Channel: Master -> Script
 | 
			
		||||
    let (tx_master, rx_script) = std::sync::mpsc::channel();
 | 
			
		||||
 | 
			
		||||
    // Spawn thread with Engine
 | 
			
		||||
    std::thread::spawn(move || {
 | 
			
		||||
        // Create Engine
 | 
			
		||||
        let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
        // Register API
 | 
			
		||||
        // Notice that the API functions are blocking
 | 
			
		||||
        engine.register_fn("get", move || rx_script.recv().unwrap())
 | 
			
		||||
              .register_fn("put", move |v: i64| tx_script.send(v).unwrap());
 | 
			
		||||
 | 
			
		||||
        // Run script
 | 
			
		||||
        engine.run(
 | 
			
		||||
        r#"
 | 
			
		||||
            print("Starting script loop...");
 | 
			
		||||
 | 
			
		||||
            loop {
 | 
			
		||||
                // The following call blocks until there is data
 | 
			
		||||
                // in the channel
 | 
			
		||||
                let x = get();
 | 
			
		||||
                print(`Script Read: ${x}`);
 | 
			
		||||
 | 
			
		||||
                x += 1;
 | 
			
		||||
 | 
			
		||||
                print(`Script Write: ${x}`);
 | 
			
		||||
 | 
			
		||||
                // The following call blocks until the data
 | 
			
		||||
                // is successfully sent to the channel
 | 
			
		||||
                put(x);
 | 
			
		||||
            }
 | 
			
		||||
        "#).unwrap();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // This is the main processing thread
 | 
			
		||||
 | 
			
		||||
    println!("Starting main loop...");
 | 
			
		||||
 | 
			
		||||
    let mut value = 0_i64;
 | 
			
		||||
 | 
			
		||||
    while value < 10 {
 | 
			
		||||
        println!("Value: {value}");
 | 
			
		||||
        // Send value to script
 | 
			
		||||
        tx_master.send(value).unwrap();
 | 
			
		||||
        // Receive value from script
 | 
			
		||||
        value = rx_master.recv().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Considerations for [`sync`]
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
`std::mpsc::Sender` and `std::mpsc::Receiver` are not `Sync`, therefore they cannot be used in
 | 
			
		||||
registered functions if the [`sync`] feature is enabled.
 | 
			
		||||
 | 
			
		||||
In that situation, it is possible to wrap the `Sender` and `Receiver` each in a `Mutex` or `RwLock`,
 | 
			
		||||
which makes them `Sync`.
 | 
			
		||||
 | 
			
		||||
This, however, incurs the additional overhead of locking and unlocking the `Mutex` or `RwLock`
 | 
			
		||||
during every function call, which is technically not necessary because there are no other references
 | 
			
		||||
to them.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish note "Async"
 | 
			
		||||
 | 
			
		||||
The example above highlights the fact that Rhai scripts can call any Rust function, including ones
 | 
			
		||||
that are _blocking_.
 | 
			
		||||
 | 
			
		||||
However, Rhai is essentially a blocking, single-threaded engine. Therefore it does _not_ provide an
 | 
			
		||||
async API.
 | 
			
		||||
 | 
			
		||||
That means, although it is simple to use Rhai within a multi-threading environment where blocking a
 | 
			
		||||
thread is acceptable or even expected, it is currently not possible to call _async_ functions within
 | 
			
		||||
Rhai scripts because there is no mechanism in [`Engine`] to wrap the state of the call stack inside
 | 
			
		||||
a future.
 | 
			
		||||
 | 
			
		||||
Fortunately an [`Engine`] is re-entrant so it can be shared among many async tasks. It is usually
 | 
			
		||||
possible to split a script into multiple parts to avoid having to call async functions.
 | 
			
		||||
 | 
			
		||||
Creating an [`Engine`] is also relatively cheap (extremely cheap if creating a [raw `Engine`]),
 | 
			
		||||
so it is also a valid pattern to spawn a new [`Engine`] instance for each task.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,95 +0,0 @@
 | 
			
		||||
Multiple Instantiation
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
Rhai's [features] are not strictly additive.  This is easily deduced from the [`no_std`] feature
 | 
			
		||||
which prepares the crate for `no-std` builds.  Obviously, turning on this feature has a material
 | 
			
		||||
impact on how Rhai behaves.
 | 
			
		||||
 | 
			
		||||
Many crates resolve this by going the opposite direction: build for `no-std` in default, but add a
 | 
			
		||||
`std` feature, included by default, which builds for the `stdlib`.
 | 
			
		||||
 | 
			
		||||
Rhai, however, is more complex.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Rhai Language Features Are Not Additive
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
Language features cannot be easily made _additive_.
 | 
			
		||||
 | 
			
		||||
That is because the _lack_ of a language feature is a feature by itself.
 | 
			
		||||
 | 
			
		||||
```admonish question "A practical illustration"
 | 
			
		||||
 | 
			
		||||
Assume an _additive_ feature called `floating-point` that adds floating-point support.
 | 
			
		||||
 | 
			
		||||
Assume also that the application _omits_ the `floating-point` feature (why? perhaps integers are all
 | 
			
		||||
that make sense within the project domain). Floating-point numbers do not even parse under this
 | 
			
		||||
configuration and will generate syntax errors.
 | 
			
		||||
 | 
			
		||||
Now, assume that a dependent crate _also_ depends on Rhai, but a new version suddenly decides to
 | 
			
		||||
_require_ floating-point support. That dependent crate would, naturally, specify the
 | 
			
		||||
`floating-point` feature.
 | 
			
		||||
 | 
			
		||||
Under such circumstances, unless _exact_ versioning is used and the dependent crate depends on a
 | 
			
		||||
_different_ version of Rhai, Cargo automatically _merges_ both dependencies, with the `floating-point`
 | 
			
		||||
feature turned on because features are _additive_.
 | 
			
		||||
 | 
			
		||||
This will in turn break the application, which by itself specifically omits `floating-point` and
 | 
			
		||||
expects floating-point numbers to be rejected, in unexpected ways. Suddenly and without warning,
 | 
			
		||||
floating-point numbers show up in data which the application is not prepared to handle.
 | 
			
		||||
 | 
			
		||||
There is no way out of this dilemma, because the _lack_ of a language feature can be depended upon
 | 
			
		||||
as a feature.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Multiple Instantiations of Rhai Within The Same Project
 | 
			
		||||
-------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
The trick is to differentiate between multiple identical copies of Rhai, each having
 | 
			
		||||
a different [features] set, by their _sources_:
 | 
			
		||||
 | 
			
		||||
* Different versions from [`crates.io`](https://crates.io/crates/rhai/) – The official crate.
 | 
			
		||||
 | 
			
		||||
* Different releases from [`GitHub`](https://github.com/rhaiscript/rhai) – Crate source on GitHub.
 | 
			
		||||
 | 
			
		||||
* Forked copy of [https://github.com/rhaiscript/rhai](https://github.com/rhaiscript/rhai) on GitHub.
 | 
			
		||||
 | 
			
		||||
* Local copy of [https://github.com/rhaiscript/rhai](https://github.com/rhaiscript/rhai) downloaded form GitHub.
 | 
			
		||||
 | 
			
		||||
Use the following configuration in `Cargo.toml` to pull in multiple copies of Rhai within the same project:
 | 
			
		||||
 | 
			
		||||
```toml
 | 
			
		||||
[dependencies]
 | 
			
		||||
rhai = { version = "{{version}}", features = [ "no_float" ] }
 | 
			
		||||
rhai_github = { git = "https://github.com/rhaiscript/rhai", features = [ "unchecked" ] }
 | 
			
		||||
rhai_my_github = { git = "https://github.com/my_github/rhai", branch = "variation1", features = [ "serde", "no_closure" ] }
 | 
			
		||||
rhai_local = { path = "../rhai_copy" }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The example above creates four different modules: `rhai`, `rhai_github`, `rhai_my_github` and
 | 
			
		||||
`rhai_local`, each referring to a different Rhai copy with the appropriate [features] set.
 | 
			
		||||
 | 
			
		||||
Only one crate of any particular version can be used from each source, because Cargo merges all
 | 
			
		||||
candidate cases within the same source, adding all [features] together.
 | 
			
		||||
 | 
			
		||||
If more than four different instantiations of Rhai is necessary (why?), create more local
 | 
			
		||||
repositories or GitHub forks or branches.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish danger.small "No way To avoid dependency conflicts"
 | 
			
		||||
 | 
			
		||||
Unfortunately, pulling in Rhai from different sources do not resolve the problem of [features]
 | 
			
		||||
conflict between dependencies.  Even overriding `crates.io` via the `[patch]` manifest section
 | 
			
		||||
doesn't work – all dependencies will eventually find the only one copy.
 | 
			
		||||
 | 
			
		||||
What is necessary – multiple copies of Rhai, one for each dependent crate that requires it,
 | 
			
		||||
together with their _unique_ [features] set intact.  In other words, turning off Cargo's crate
 | 
			
		||||
merging feature _just for Rhai_.
 | 
			
		||||
 | 
			
		||||
Unfortunately, as of this writing, there is no known method to achieve it.
 | 
			
		||||
 | 
			
		||||
Therefore, moral of the story: avoid pulling in multiple crates that depend on Rhai.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,84 +0,0 @@
 | 
			
		||||
Objects with Defined Behaviors
 | 
			
		||||
==============================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* To simulate specific _object types_ coded in Rust with fields and methods.
 | 
			
		||||
 | 
			
		||||
* Native Rust methods (not scripted) are pre-defined for these types.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Use an [object map] as the main container of the type.
 | 
			
		||||
 | 
			
		||||
* Create [function pointers] binding to _native Rust functions_ and store them as properties
 | 
			
		||||
  of the [object map].
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Rhai does not have _objects_ per se and is not object-oriented (in the traditional sense), but it is
 | 
			
		||||
possible to _simulate_ object-oriented programming via [object maps].
 | 
			
		||||
 | 
			
		||||
When using [object maps] to simulate objects (See [here](oop.md) for more details), a property that
 | 
			
		||||
holds a [function pointer] can be called like a method function with the [object map] being the
 | 
			
		||||
object of the method call.
 | 
			
		||||
 | 
			
		||||
It is also possible to create [function pointers] that bind to native Rust functions/closures so
 | 
			
		||||
that those are called when the [function pointers] are called.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
A [function pointer] can be created that binds to a specific Rust function or closure.
 | 
			
		||||
 | 
			
		||||
(See [here]({{rootUrl}}/language/fn-ptr.md#bind-to-a-native-rust-function) for details on using
 | 
			
		||||
`FnPtr::from_fn` and `FnPtr::from_dyn_fn`).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// This is the pre-defined behavior for the 'Awesome' object type.
 | 
			
		||||
fn my_awesome_fn(ctx: NativeCallContext, args: &mut[&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
 | 
			
		||||
    // Check number of arguments
 | 
			
		||||
    if args.len() != 2 {
 | 
			
		||||
        return Err("one argument is required, plus the object".into());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get call arguments
 | 
			
		||||
    let x = args[1].try_cast::<i64>().map_err(|_| "argument must be an integer".into())?;
 | 
			
		||||
 | 
			
		||||
    // Get mutable reference to the object map, which is passed as the first argument
 | 
			
		||||
    let map = &mut *args[0].as_map_mut().map_err(|_| "object must be a map".into())?;
 | 
			
		||||
 | 
			
		||||
    // Do something awesome here ...
 | 
			
		||||
    let result = ...
 | 
			
		||||
 | 
			
		||||
    Ok(result.into())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register a function to create a pre-defined object
 | 
			
		||||
engine.register_fn("create_awesome_object", || {
 | 
			
		||||
    // Use an object map as base
 | 
			
		||||
    let mut map = Map::new();
 | 
			
		||||
 | 
			
		||||
    // Create a function pointer that binds to 'my_awesome_fn'
 | 
			
		||||
    let fp = FnPtr::from_fn("awesome", my_awesome_fn)?;
 | 
			
		||||
    //                      ^ name of method
 | 
			
		||||
    //                                 ^ native function
 | 
			
		||||
 | 
			
		||||
    // Store the function pointer in the object map
 | 
			
		||||
    map.insert("awesome".into(), fp.into());
 | 
			
		||||
 | 
			
		||||
    Ok(Dynamic::from_map(map))
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The [object map] can then be used just like any object-oriented object.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let obj = create_awesome_object();
 | 
			
		||||
 | 
			
		||||
let result = obj.awesome(42);   // calls 'my_awesome_fn' with '42' as argument
 | 
			
		||||
```
 | 
			
		||||
@@ -1,196 +0,0 @@
 | 
			
		||||
Object-Oriented Programming (OOP)
 | 
			
		||||
=================================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
Rhai does not have _objects_ per se and is not object-oriented (in the traditional sense),
 | 
			
		||||
but it is possible to _simulate_ object-oriented programming.
 | 
			
		||||
 | 
			
		||||
```admonish question.small "To OOP or not to OOP, that is the question."
 | 
			
		||||
 | 
			
		||||
Regardless of whether object-oriented programming (OOP) should be treated as a pattern or
 | 
			
		||||
an _anti-pattern_ (the programming world is split 50-50 on this), there are always users who
 | 
			
		||||
would like to write Rhai in "the OOP way."
 | 
			
		||||
 | 
			
		||||
Rust itself is not object-oriented in the traditional sense; JavaScript also isn't, but that didn't
 | 
			
		||||
prevent generations of programmers trying to shoehorn a class-based inheritance system onto it.
 | 
			
		||||
 | 
			
		||||
So... as soon as Rhai gained in usage, way way before version 1.0, PR's started coming in to make
 | 
			
		||||
it possible to write Rhai in "the OOP way."
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use Object Maps to Simulate OOP
 | 
			
		||||
-------------------------------
 | 
			
		||||
 | 
			
		||||
Rhai's [object maps] has [special support for OOP]({{rootUrl}}/language/object-maps-oop.md).
 | 
			
		||||
 | 
			
		||||
| Rhai concept                                          | Maps to OOP |
 | 
			
		||||
| ----------------------------------------------------- | :---------: |
 | 
			
		||||
| [Object maps]                                         |   objects   |
 | 
			
		||||
| [Object map] properties holding values                | properties  |
 | 
			
		||||
| [Object map] properties that hold [function pointers] |   methods   |
 | 
			
		||||
 | 
			
		||||
When a property of an [object map] is called like a method function, and if it happens to hold a
 | 
			
		||||
valid [function pointer] (perhaps defined via an [anonymous function] or more commonly as a [closure]),
 | 
			
		||||
then the call will be dispatched to the actual function with `this` binding to the
 | 
			
		||||
[object map] itself.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use Closures to Define Methods
 | 
			
		||||
------------------------------
 | 
			
		||||
 | 
			
		||||
[Closures] defined as values for [object map] properties take on a syntactic shape which resembles
 | 
			
		||||
very closely that of class methods in an OOP language.
 | 
			
		||||
 | 
			
		||||
[Closures] also _capture_ [variables] from the defining environment, which is a very common language
 | 
			
		||||
feature.  It can be turned off via the [`no_closure`] feature.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let factor = 1;
 | 
			
		||||
 | 
			
		||||
// Define the object
 | 
			
		||||
let obj = #{
 | 
			
		||||
        data: 0,                            // object field
 | 
			
		||||
        increment: |x| this.data += x,      // 'this' binds to 'obj'
 | 
			
		||||
        update: |x| this.data = x * factor, // 'this' binds to 'obj', 'factor' is captured
 | 
			
		||||
        action: || print(this.data)         // 'this' binds to 'obj'
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
// Use the object
 | 
			
		||||
obj.increment(1);
 | 
			
		||||
obj.action();                               // prints 1
 | 
			
		||||
 | 
			
		||||
obj.update(42);
 | 
			
		||||
obj.action();                               // prints 42
 | 
			
		||||
 | 
			
		||||
factor = 2;
 | 
			
		||||
 | 
			
		||||
obj.update(42);
 | 
			
		||||
obj.action();                               // prints 84
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Simulating Inheritance with Polyfills
 | 
			
		||||
-------------------------------------
 | 
			
		||||
 | 
			
		||||
The `fill_with` method of [object maps] can be conveniently used to _polyfill_ default method
 | 
			
		||||
implementations from a _base class_, as per OOP lingo.
 | 
			
		||||
 | 
			
		||||
Do not use the `mixin` method because it _overwrites_ existing fields.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Define base class
 | 
			
		||||
let BaseClass = #{
 | 
			
		||||
    factor: 1,
 | 
			
		||||
    data: 42,
 | 
			
		||||
 | 
			
		||||
    get_data: || this.data * 2,
 | 
			
		||||
    update: |x| this.data += x * this.factor
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let obj = #{
 | 
			
		||||
    // Override base class field
 | 
			
		||||
    factor: 100,
 | 
			
		||||
 | 
			
		||||
    // Override base class method
 | 
			
		||||
    // Notice that the base class can also be accessed, if in scope
 | 
			
		||||
    get_data: || this.call(BaseClass.get_data) * 999,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Polyfill missing fields/methods
 | 
			
		||||
obj.fill_with(BaseClass);
 | 
			
		||||
 | 
			
		||||
// By this point, 'obj' has the following:
 | 
			
		||||
//
 | 
			
		||||
// #{
 | 
			
		||||
//      factor: 100
 | 
			
		||||
//      data: 42,
 | 
			
		||||
//      get_data: || this.call(BaseClass.get_data) * 999,
 | 
			
		||||
//      update: |x| this.data += x * this.factor
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// obj.get_data() => (this.data (42) * 2) * 999
 | 
			
		||||
obj.get_data() == 83916;
 | 
			
		||||
 | 
			
		||||
obj.update(1);
 | 
			
		||||
 | 
			
		||||
obj.data == 142
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Prototypical Inheritance via Mixin
 | 
			
		||||
----------------------------------
 | 
			
		||||
 | 
			
		||||
Some languages like JavaScript has _prototypical_ inheritance, which bases inheritance on a
 | 
			
		||||
_prototype_ object.
 | 
			
		||||
 | 
			
		||||
It is possible to simulate this form of inheritance using [object maps], leveraging the fact that,
 | 
			
		||||
in Rhai, all values are cloned and there are no pointers. This significantly simplifies coding logic.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Define prototype 'class'
 | 
			
		||||
 | 
			
		||||
const PrototypeClass = #{
 | 
			
		||||
    field: 42,
 | 
			
		||||
 | 
			
		||||
    get_field: || this.field,
 | 
			
		||||
    set_field: |x| this.field = x
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Create instances of the 'class'
 | 
			
		||||
 | 
			
		||||
let obj1 = PrototypeClass;                  // a copy of 'PrototypeClass'
 | 
			
		||||
 | 
			
		||||
obj1.get_field() == 42;
 | 
			
		||||
 | 
			
		||||
let obj2 = PrototypeClass;                  // a copy of 'PrototypeClass'
 | 
			
		||||
 | 
			
		||||
obj2.mixin(#{                               // override fields and methods
 | 
			
		||||
    field: 1,
 | 
			
		||||
    get_field: || this.field * 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
obj2.get_field() == 2;
 | 
			
		||||
 | 
			
		||||
let obj2 = PrototypeClass + #{              // compact syntax with '+'
 | 
			
		||||
    field: 1,
 | 
			
		||||
    get_field: || this.field * 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
obj2.get_field() == 2;
 | 
			
		||||
 | 
			
		||||
// Inheritance chain
 | 
			
		||||
 | 
			
		||||
const ParentClass = #{
 | 
			
		||||
    field: 123,
 | 
			
		||||
    new_field: 0,
 | 
			
		||||
    action: || print(this.new_field * this.field)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ChildClass = #{
 | 
			
		||||
    action: || {
 | 
			
		||||
        this.field = this.new_field;
 | 
			
		||||
        this.new_field = ();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let obj3 = PrototypeClass + ParentClass + ChildClass;
 | 
			
		||||
 | 
			
		||||
// Alternate formulation
 | 
			
		||||
 | 
			
		||||
const ParentClass = PrototypeClass + #{
 | 
			
		||||
    field: 123,
 | 
			
		||||
    new_field: 0,
 | 
			
		||||
    action: || print(this.new_field * this.field)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ChildClass = ParentClass + #{
 | 
			
		||||
    action: || {
 | 
			
		||||
        this.field = this.new_field;
 | 
			
		||||
        this.new_field = ();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let obj3 = ChildClass;                      // a copy of 'ChildClass'
 | 
			
		||||
```
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
One `Engine` Instance Per Call
 | 
			
		||||
==============================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system where scripts are called a _lot_, in tight loops or in parallel.
 | 
			
		||||
 | 
			
		||||
* Keeping a global [`Engine`] instance is sub-optimal due to contention and locking.
 | 
			
		||||
 | 
			
		||||
* Scripts need to be executed independently from each other, perhaps concurrently.
 | 
			
		||||
 | 
			
		||||
* Scripts are used to [create Rust closures][`Func`] that are stored and may be called at any time,
 | 
			
		||||
  perhaps concurrently. In this case, the [`Engine`] instance is usually moved into the closure itself.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Rhai's [`AST`] structure is sharable – meaning that one copy of the [`AST`] can be run on
 | 
			
		||||
  multiple instances of [`Engine`] simultaneously.
 | 
			
		||||
 | 
			
		||||
* Rhai's [packages] and [modules] are also sharable.
 | 
			
		||||
 | 
			
		||||
* This means that [`Engine`] instances can be _decoupled_ from the base system ([packages] and
 | 
			
		||||
  [modules]) as well as the scripts ([`AST`]) so they can be created very cheaply.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Procedure
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
* Gather up all common custom functions into a [custom package].
 | 
			
		||||
 | 
			
		||||
  * This [custom package] should also include standard [packages] needed. For example, to duplicate
 | 
			
		||||
    `Engine::new`, use a [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md).
 | 
			
		||||
  
 | 
			
		||||
  * [Packages] are sharable, so using a [custom package] is _much cheaper_ than registering all the
 | 
			
		||||
    functions one by one.
 | 
			
		||||
 | 
			
		||||
* Store a global [`AST`] for use with all [`Engine`] instances.
 | 
			
		||||
 | 
			
		||||
* Always use `Engine::new_raw` to create a [raw `Engine`], instead of `Engine::new` which is _much_
 | 
			
		||||
  more expensive. A [raw `Engine`] is _extremely_ cheap to create.
 | 
			
		||||
 | 
			
		||||
* Register the [custom package] with the [raw `Engine`] via `Package::register_into_engine`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::def_package;
 | 
			
		||||
use rhai::packages::{Package, StandardPackage};
 | 
			
		||||
use rhai::FuncRegistration;
 | 
			
		||||
 | 
			
		||||
// Define the custom package 'MyCustomPackage'.
 | 
			
		||||
def_package! {
 | 
			
		||||
    /// My own personal super-duper custom package
 | 
			
		||||
    // Aggregate other base packages simply by listing them after the colon.
 | 
			
		||||
    pub MyCustomPackage(module) : StandardPackage {
 | 
			
		||||
      // Register additional Rust functions.
 | 
			
		||||
      FuncRegistration::new("foo")
 | 
			
		||||
          .with_params(&["s: ImmutableString", "i64"])
 | 
			
		||||
          .set_into_module(module, |s: ImmutableString| foo(s.into_owned()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let ast = /* ... some AST ... */;
 | 
			
		||||
 | 
			
		||||
let my_custom_package = MyCustomPackage::new();
 | 
			
		||||
 | 
			
		||||
// The following loop creates 10,000 Engine instances!
 | 
			
		||||
 | 
			
		||||
for x in 0..10_000 {
 | 
			
		||||
    // Create a raw Engine - extremely cheap
 | 
			
		||||
    let mut engine = Engine::new_raw();
 | 
			
		||||
 | 
			
		||||
    // Register custom package - cheap
 | 
			
		||||
    my_custom_package.register_into_engine(&mut engine);
 | 
			
		||||
 | 
			
		||||
    // Evaluate script
 | 
			
		||||
    engine.run_ast(&ast)?;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,164 +0,0 @@
 | 
			
		||||
Passing External References to Rhai (Unsafe)
 | 
			
		||||
============================================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[`transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html
 | 
			
		||||
[`Engine::set_default_tag`]: https://docs.rs/rhai/{{version}}/rhai/struct.Engine.html#method.set_default_tag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish danger "Don't Do It™"
 | 
			
		||||
 | 
			
		||||
As with anything `unsafe`, don't do this unless you have exhausted all possible alternatives.
 | 
			
		||||
 | 
			
		||||
There are usually alternatives.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system where an embedded [`Engine`] is used to call scripts within a callback function/closure
 | 
			
		||||
  from some external system.
 | 
			
		||||
  
 | 
			
		||||
  This is extremely common when working with an ECS (Entity Component System) or a GUI library where the script
 | 
			
		||||
  must draw via the provided graphics context.
 | 
			
		||||
 | 
			
		||||
* Said external system provides only _references_ (mutable or immutable) to work with their internal states.
 | 
			
		||||
 | 
			
		||||
* Such states are not available outside of references (and therefore cannot be made shared).
 | 
			
		||||
 | 
			
		||||
* Said external system's API require such states to function.
 | 
			
		||||
 | 
			
		||||
* It is impossible to pass references into Rhai because the [`Engine`] does not track lifetimes.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Make a newtype that wraps an integer which is set to the CPU's pointer size (typically `usize`).
 | 
			
		||||
 | 
			
		||||
* [`transmute`] the reference provided by the system into an integer and store it within the newtype.
 | 
			
		||||
  This requires `unsafe` code.
 | 
			
		||||
 | 
			
		||||
* Use the newtype as a _handle_ for all registered API functions, [`transmute`] the integer back
 | 
			
		||||
  to a reference before use. This also requires `unsafe` code.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish bug "Here be dragons..."
 | 
			
		||||
 | 
			
		||||
Make **absolutely sure** that the newtype is never stored anywhere permanent (e.g. in a [`Scope`])
 | 
			
		||||
nor does it ever live outside of the reference's scope!
 | 
			
		||||
 | 
			
		||||
Otherwise, a crash is the system being nice to you...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
/// Newtype wrapping a reference (pointer) cast into 'usize'
 | 
			
		||||
/// together with a unique ID for protection.
 | 
			
		||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
 | 
			
		||||
pub struct WorldHandle(usize, i64);
 | 
			
		||||
 | 
			
		||||
/// Create handle from reference
 | 
			
		||||
impl From<&mut World> for WorldHandle {
 | 
			
		||||
    fn from(world: &mut World) -> Self {
 | 
			
		||||
        Self::new(world)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Recover reference from handle
 | 
			
		||||
impl AsMut<World> for WorldHandle {
 | 
			
		||||
    fn as_mut(&mut self) -> &mut World {
 | 
			
		||||
        unsafe { std::mem::transmute(self.0) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WorldHandle {
 | 
			
		||||
    /// Create handle from reference, using a random number as unique ID
 | 
			
		||||
    pub fn new(world: &mut World) -> Self {
 | 
			
		||||
        let handle =unsafe { std::mem::transmute(world) };
 | 
			
		||||
        let unique_id = rand::random();
 | 
			
		||||
 | 
			
		||||
        Self(handle, unique_id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the unique ID of this instance
 | 
			
		||||
    pub fn unique_id(&self) -> i64 {
 | 
			
		||||
        self.1
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// API for handle to 'World'
 | 
			
		||||
#[export_module]
 | 
			
		||||
pub mod handle_module {
 | 
			
		||||
    pub type Handle = WorldHandle;
 | 
			
		||||
 | 
			
		||||
    /// Draw a bunch of pretty shapes.
 | 
			
		||||
    #[rhai_fn(return_raw)]
 | 
			
		||||
    pub fn draw(context: NativeCallContext, handle: &mut Handle, shapes: Array) -> Result<(), Box<EvalAltResult>> {
 | 
			
		||||
        // Double check the pointer is still fresh
 | 
			
		||||
        // by comparing the handle's unique ID with
 | 
			
		||||
        // the version stored in the engine's tag!
 | 
			
		||||
        if handle.unique_id() != context.tag() {
 | 
			
		||||
            return "Ouch! The handle is stale!".into();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get the reference to 'World'
 | 
			
		||||
        let world: &mut World = handle.as_mut();
 | 
			
		||||
 | 
			
		||||
        // ... work with reference
 | 
			
		||||
        world.draw_really_pretty_shapes(shapes);
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register API for handle type
 | 
			
		||||
engine.register_global_module(exported_module!(handle_module).into());
 | 
			
		||||
 | 
			
		||||
// Successfully pass a reference to 'World' into Rhai
 | 
			
		||||
super_ecs_system.query(...).for_each(|world: &mut World| {
 | 
			
		||||
    // Create handle from reference to 'World'
 | 
			
		||||
    let handle: WorldHandle = world.into();
 | 
			
		||||
 | 
			
		||||
    // Set the handle's ID into the engine's tag.
 | 
			
		||||
    // Alternatively, use 'Engine::call_fn_with_options'.
 | 
			
		||||
    engine.set_default_tag(handle.unique_id());
 | 
			
		||||
 | 
			
		||||
    // Add handle into scope
 | 
			
		||||
    let mut scope = Scope::new();
 | 
			
		||||
    scope.push("world", handle);
 | 
			
		||||
    
 | 
			
		||||
    // Work with handle
 | 
			
		||||
    engine.run_with_scope(&mut scope, "world.draw([1, 2, 3])")
 | 
			
		||||
 | 
			
		||||
    // Make sure no instance of 'handle' leaks outside this scope!
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish bug "Here be Many Dragons!"
 | 
			
		||||
 | 
			
		||||
It is of utmost importance that no instance of the handle newtype ever **_leaks_** outside of the
 | 
			
		||||
appropriate scope where the wrapped reference may no longer be valid.
 | 
			
		||||
 | 
			
		||||
For example, do not allow the script to store a copy of the handle anywhere that can
 | 
			
		||||
potentially be persistent (e.g. within an [object map]).
 | 
			
		||||
 | 
			
		||||
#### Safety check via unique ID
 | 
			
		||||
 | 
			
		||||
One solution, as illustrated in the example, is to always tag each handle instance together with
 | 
			
		||||
a unique random ID. That same ID can then be set into the [`Engine`] (via [`Engine::set_default_tag`])
 | 
			
		||||
before running scripts.
 | 
			
		||||
 | 
			
		||||
Before executing any API function, first check whether the handle's ID matches that of the current [`Engine`]
 | 
			
		||||
(via [`NativeCallContext::tag`][`NativeCallContext`]). If they differ, the handle is stale and should never be used;
 | 
			
		||||
an error should be returned instead.
 | 
			
		||||
 | 
			
		||||
#### Alternative to `Engine::set_default_tag`
 | 
			
		||||
 | 
			
		||||
Alternatively, if the [`Engine`] cannot be made mutable, use `Engine::call_fn_with_options`
 | 
			
		||||
to set the ID before directly calling a script [function] in a compiled [`AST`].
 | 
			
		||||
```
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
Serialize an AST
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
In many situations, it is tempting to _serialize_ an [`AST`], so that it can be loaded and recreated
 | 
			
		||||
later on.
 | 
			
		||||
 | 
			
		||||
In Rhai, there is usually little reason to do so.
 | 
			
		||||
 | 
			
		||||
```admonish failure.small "Don't Do This"
 | 
			
		||||
 | 
			
		||||
Serialize the [`AST`] into some format for storage.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish success.small "Do This"
 | 
			
		||||
 | 
			
		||||
Store a copy of the original script, preferably compressed.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Storing the original script text, preferably compressed (via `gzip` etc.) usually yields much smaller data size.
 | 
			
		||||
 | 
			
		||||
Plus, is it possibly faster to recompile the original script than to recreate the [`AST`] via
 | 
			
		||||
deserialization.
 | 
			
		||||
 | 
			
		||||
That is because the deserialization processing is essentially a form of parsing, in this case
 | 
			
		||||
parsing the serialized data into an [`AST`] – an equivalent process to what Rhai does, which
 | 
			
		||||
is parsing the script text into the same [`AST`].
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Illustration
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
The following script occupies only 42 bytes, possibly less if compressed.
 | 
			
		||||
That is only slightly more than 5 words on a 64-bit CPU!
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn sum(x, y) { x + y }
 | 
			
		||||
print(sum(42, 1));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The [`AST`] would be _much_ more complicated and looks something like this:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
FnDef {
 | 
			
		||||
    Name: "sum",
 | 
			
		||||
    ThisType: None,
 | 
			
		||||
    Parameters: [
 | 
			
		||||
        "x",
 | 
			
		||||
        "y"
 | 
			
		||||
    ],
 | 
			
		||||
    Body: Block [
 | 
			
		||||
        Return {
 | 
			
		||||
            Expression {
 | 
			
		||||
                FnCall {
 | 
			
		||||
                    Name: "+",
 | 
			
		||||
                    Arguments: [
 | 
			
		||||
                        Variable "x",
 | 
			
		||||
                        Variable "y"
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
Block [
 | 
			
		||||
    FnCall {
 | 
			
		||||
        Name: "print",
 | 
			
		||||
        Arguments: [
 | 
			
		||||
            Expression {
 | 
			
		||||
                FnCall {
 | 
			
		||||
                    Name: "sum",
 | 
			
		||||
                    Arguments: [
 | 
			
		||||
                        Constant 42,
 | 
			
		||||
                        Constant 1
 | 
			
		||||
                    ]
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
which would take _much_ more space to serialize.
 | 
			
		||||
 | 
			
		||||
For instance, the constant 1 alone would take up 8 bytes, while the script text takes up only one byte!
 | 
			
		||||
@@ -1,221 +0,0 @@
 | 
			
		||||
Singleton Command Object
 | 
			
		||||
========================
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Usage scenario"
 | 
			
		||||
 | 
			
		||||
* A system provides core functionalities, but no driving logic.
 | 
			
		||||
 | 
			
		||||
* The driving logic must be dynamic and hot-loadable.
 | 
			
		||||
 | 
			
		||||
* A script is used to drive the system and provide control intelligence.
 | 
			
		||||
 | 
			
		||||
* The API is multiplexed, meaning that it can act on multiple system-provided entities, or
 | 
			
		||||
 | 
			
		||||
* The API lends itself readily to an object-oriented (OO) representation.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish abstract "Key concepts"
 | 
			
		||||
 | 
			
		||||
* Expose a Command type with an API.  The [`no_object`] feature must not be on.
 | 
			
		||||
 | 
			
		||||
* Leverage [function overloading] to simplify the API design.
 | 
			
		||||
 | 
			
		||||
* Since Rhai is _[sand-boxed]_, it cannot mutate anything outside of its internal environment.
 | 
			
		||||
  To perform external actions via an API, the command object type must be wrapped in a `RefCell`
 | 
			
		||||
  (or `RwLock`/`Mutex` for [`sync`]) and shared to the [`Engine`].
 | 
			
		||||
 | 
			
		||||
* Load each command object into a custom [`Scope`] as constant variables.
 | 
			
		||||
 | 
			
		||||
* Control each command object in script via the constants.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Implementation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
There are two broad ways for Rhai to control an external system, both of which involve wrapping the
 | 
			
		||||
system in a shared, interior-mutated object.
 | 
			
		||||
 | 
			
		||||
This is the other way which involves directly exposing the data structures of the external system as
 | 
			
		||||
a name singleton object in the scripting space.
 | 
			
		||||
 | 
			
		||||
Use this when the API is complex but has a clear object structure.
 | 
			
		||||
 | 
			
		||||
For a relatively simple API that is action-based and not object-based, use the
 | 
			
		||||
[Control Layer]({{rootUrl}}/patterns/control.md) pattern instead.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Functional API
 | 
			
		||||
 | 
			
		||||
Assume the following command object type:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
struct EnergizerBunny { ... }
 | 
			
		||||
 | 
			
		||||
impl EnergizerBunny {
 | 
			
		||||
    pub fn new () -> Self { ... }
 | 
			
		||||
    pub fn go (&mut self) { ... }
 | 
			
		||||
    pub fn stop (&mut self) { ... }
 | 
			
		||||
    pub fn is_going (&self) -> bool { ... }
 | 
			
		||||
    pub fn get_speed (&self) -> i64 { ... }
 | 
			
		||||
    pub fn set_speed (&mut self, speed: i64) { ... }
 | 
			
		||||
    pub fn turn (&mut self, left_turn: bool) { ... }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Wrap command object type as shared
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
pub type SharedBunny = Rc<RefCell<EnergizerBunny>>;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
or in multi-threaded environments with the [`sync`] feature, use one of the following:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
pub type SharedBunny = Arc<RwLock<EnergizerBunny>>;
 | 
			
		||||
 | 
			
		||||
pub type SharedBunny = Arc<Mutex<EnergizerBunny>>;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Develop a plugin with methods and getters/setters
 | 
			
		||||
 | 
			
		||||
The easiest way to develop a complete set of API for a [custom type] is via a [plugin module].
 | 
			
		||||
 | 
			
		||||
Notice that putting `pure` in `#[rhai_fn(...)]` allows a [getter/setter][getters/setters] to operate
 | 
			
		||||
on a [constant] without raising an error.  Therefore, it is needed on _all_ functions.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::plugin::*;
 | 
			
		||||
 | 
			
		||||
// Remember to put 'pure' on all functions, or they'll choke on constants!
 | 
			
		||||
 | 
			
		||||
#[export_module]
 | 
			
		||||
pub mod bunny_api {
 | 
			
		||||
    // Custom type 'SharedBunny' will be called 'EnergizerBunny' in scripts
 | 
			
		||||
    pub type EnergizerBunny = SharedBunny;
 | 
			
		||||
 | 
			
		||||
    // This constant is also available to scripts
 | 
			
		||||
    pub const MAX_SPEED: i64 = 100;
 | 
			
		||||
 | 
			
		||||
    #[rhai_fn(get = "power", pure)]
 | 
			
		||||
    pub fn get_power(bunny: &mut EnergizerBunny) -> bool {
 | 
			
		||||
        bunny.borrow().is_going()
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(set = "power", pure)]
 | 
			
		||||
    pub fn set_power(bunny: &mut EnergizerBunny, on: bool) {
 | 
			
		||||
        if on {
 | 
			
		||||
            if bunny.borrow().is_going() {
 | 
			
		||||
                println!("Still going...");
 | 
			
		||||
            } else {
 | 
			
		||||
                bunny.borrow_mut().go();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if bunny.borrow().is_going() {
 | 
			
		||||
                bunny.borrow_mut().stop();
 | 
			
		||||
            } else {
 | 
			
		||||
                println!("Already out of battery!");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(get = "speed", pure)]
 | 
			
		||||
    pub fn get_speed(bunny: &mut EnergizerBunny) -> i64 {
 | 
			
		||||
        if bunny.borrow().is_going() {
 | 
			
		||||
            bunny.borrow().get_speed()
 | 
			
		||||
        } else {
 | 
			
		||||
            0
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(set = "speed", pure, return_raw)]
 | 
			
		||||
    pub fn set_speed(bunny: &mut EnergizerBunny, speed: i64)
 | 
			
		||||
            -> Result<(), Box<EvalAltResult>>
 | 
			
		||||
    {
 | 
			
		||||
        if speed <= 0 {
 | 
			
		||||
            Err("Speed must be positive!".into())
 | 
			
		||||
        } else if speed > MAX_SPEED {
 | 
			
		||||
            Err("Bunny will be going too fast!".into())
 | 
			
		||||
        } else if !bunny.borrow().is_going() {
 | 
			
		||||
            Err("Bunny is not yet going!".into())
 | 
			
		||||
        } else {
 | 
			
		||||
            b.borrow_mut().set_speed(speed);
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(pure)]
 | 
			
		||||
    pub fn turn_left(bunny: &mut EnergizerBunny) {
 | 
			
		||||
        if bunny.borrow().is_going() {
 | 
			
		||||
            bunny.borrow_mut().turn(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[rhai_fn(pure)]
 | 
			
		||||
    pub fn turn_right(bunny: &mut EnergizerBunny) {
 | 
			
		||||
        if bunny.borrow().is_going() {
 | 
			
		||||
            bunny.borrow_mut().turn(false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
engine.register_global_module(exported_module!(bunny_api).into());
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Friendly name for custom type"
 | 
			
		||||
 | 
			
		||||
It is customary to register a _friendly display name_ for any [custom type]
 | 
			
		||||
involved in the plugin.
 | 
			
		||||
 | 
			
		||||
This can easily be done via a _type alias_ in the plugin module.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Compile script into AST
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let ast = engine.compile(script)?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Push constant command object into custom scope and run AST
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let bunny: SharedBunny = Rc::new(RefCell::new(EnergizerBunny::new()));
 | 
			
		||||
 | 
			
		||||
let mut scope = Scope::new();
 | 
			
		||||
 | 
			
		||||
// Add the singleton command object into a custom Scope.
 | 
			
		||||
// Constants, as a convention, are named with all-capital letters.
 | 
			
		||||
scope.push_constant("BUNNY", bunny.clone());
 | 
			
		||||
 | 
			
		||||
// Run the compiled AST
 | 
			
		||||
engine.run_ast_with_scope(&mut scope, &ast)?;
 | 
			
		||||
 | 
			
		||||
// Running the script directly, as below, is less desirable because
 | 
			
		||||
// the constant 'BUNNY' will be propagated and copied into each usage
 | 
			
		||||
// during the script optimization step
 | 
			
		||||
engine.run_with_scope(&mut scope, script)?;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.small "Tip: Prevent shadowing"
 | 
			
		||||
 | 
			
		||||
It is usually desirable to prevent [shadowing] of the singleton command object.
 | 
			
		||||
 | 
			
		||||
This can be easily achieved via a [variable definition filter].
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Now the script can no longer define a variable named 'BUNNY'
 | 
			
		||||
engine.on_def_var(|_, info, _| Ok(info.name != "BUNNY"));
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
### Use the command API in script
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Access the command object via constant variable 'BUNNY'.
 | 
			
		||||
 | 
			
		||||
if !BUNNY.power { BUNNY.power = true; }
 | 
			
		||||
 | 
			
		||||
if BUNNY.speed > 50 { BUNNY.speed = 50; }
 | 
			
		||||
 | 
			
		||||
BUNNY.turn_left();
 | 
			
		||||
 | 
			
		||||
let BUNNY = 42;     // <- syntax error if variable definition filter is set
 | 
			
		||||
```
 | 
			
		||||
@@ -1,104 +0,0 @@
 | 
			
		||||
Static Hashing
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
To counter [DOS] attacks, the _hasher_ used by Rhai, [`ahash`], automatically generates a different
 | 
			
		||||
_seed_ for hashing during each compilation and execution run.
 | 
			
		||||
 | 
			
		||||
This means that hash values generated by the hasher will not be _stable_ – they change
 | 
			
		||||
during each compile, and during each run.
 | 
			
		||||
 | 
			
		||||
For certain niche scenarios, however, dynamic hashes are undesirable.
 | 
			
		||||
 | 
			
		||||
So, when _static hashing_ is employed, all hashing uses a fixed, predictable seed all the time.
 | 
			
		||||
 | 
			
		||||
```admonish abstract.small "Hashing seed"
 | 
			
		||||
 | 
			
		||||
A hashing seed requires four 64-bit numbers (i.e. `u64`).
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Static hashes are consistent and predictable"
 | 
			
		||||
 | 
			
		||||
Under static hashing, the same function signature _always_ generate the same hash value,
 | 
			
		||||
given the same seed.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "Warning: Safety considerations"
 | 
			
		||||
 | 
			
		||||
A fixed hashing seed enlarges the attack surface of Rhai to malicious intent
 | 
			
		||||
(e.g. [DOS] attacks).
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Set at Run-Time
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Call the static function `rhai::config::hashing::set_ahash_seed` with four `u64` numbers that make
 | 
			
		||||
up the hashing seed.
 | 
			
		||||
 | 
			
		||||
The seed specified is always used to initialize the hasher.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Set specific hashing seed - do this BEFORE anything else!
 | 
			
		||||
rhai::config::hashing::set_ahash_seed(Some([123, 456, 789, 42]))?;
 | 
			
		||||
 | 
			
		||||
// ... from now on, all hashing will be predictable
 | 
			
		||||
let engine = Engine::new();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish danger.small "Warning: Only call once"
 | 
			
		||||
 | 
			
		||||
`rhai::config::hashing::set_ahash_seed` can only ever be called _once_,
 | 
			
		||||
and must be called _BEFORE_ performing any operation on Rhai (e.g. creating
 | 
			
		||||
an [`Engine`]).
 | 
			
		||||
 | 
			
		||||
Calling it a second time simply returns an error.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Set at Compile Time
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
The hashing seed can also be provided, at _compile time_, via the environment variable
 | 
			
		||||
`RHAI_AHASH_SEED`, with four `u64` numbers that must be specified in Rust array literal format.
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "Warning"
 | 
			
		||||
 | 
			
		||||
If a hashing seed is also set via `rhai::config::hashing::set_ahash_seed`,
 | 
			
		||||
this environment variable has no effect.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
RHAI_AHASH_SEED="[123, 456, 789, 42]" cargo build ...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The seed specified in `RHAI_AHASH_SEED` is always used to initialize the hasher.
 | 
			
		||||
 | 
			
		||||
If the environment variable is missing, or it contains all zeros (i.e. `[0, 0, 0, 0]`),
 | 
			
		||||
then static hashing is disabled.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TL;DR
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
~~~admonish question "Why can't we tell `ahash` to use a static (fixed) seed?"
 | 
			
		||||
 | 
			
		||||
For static hashing seed, [`ahash`] requires:
 | 
			
		||||
 | 
			
		||||
* `default-features = false`
 | 
			
		||||
* `runtime-rng` feature is _not_ set (default on)
 | 
			
		||||
* `compile-time-rng` feature is _not_ set
 | 
			
		||||
 | 
			
		||||
#### The bane of additive Cargo features
 | 
			
		||||
 | 
			
		||||
However, [`ahash`] is also extremely popular, used by many many other crates,
 | 
			
		||||
most notably [`hashbrown`](https://crates.io/crates/hashbrown).
 | 
			
		||||
 | 
			
		||||
Chances are that there are dependency crates that in turn depend on [`ahash`] with
 | 
			
		||||
default features. Since cargo features are _additive_, it is almost certain that [`ahash`]
 | 
			
		||||
will use a runtime-generated seed for large projects.
 | 
			
		||||
 | 
			
		||||
Hence, there exists a need to tell [`ahash`] to use a fixed seed, even when its feature flags
 | 
			
		||||
say otherwise.
 | 
			
		||||
~~~
 | 
			
		||||
		Reference in New Issue
	
	Block a user