rhai/doc/src/language/fn-namespaces.md
2020-11-06 19:17:07 +08:00

3.2 KiB

Function Namespaces

{{#include ../links.md}}

Each Function is a Separate Compilation Unit

[Functions] in Rhai are pure and they form individual compilation units. This means that individual functions can be separated, exported, re-grouped, imported, and generally mix-'n-match-ed with other completely unrelated scripts.

For example, the AST::merge and AST::combine methods (or the equivalent + and += operators) allow combining all functions in one [AST] into another, forming a new, unified, group of functions.

In general, there are two types of namespaces where functions are looked up:

Namespace Source Lookup method Sub-modules? Variables?
Global 1) Engine::register_XXX API
2) [AST] being evaluated
3) [packages] loaded
simple function name ignored ignored
Module [Module] module-qualified function name yes yes

Global Namespace

There is one global namespace for every [Engine], which includes:

  • All the native Rust functions registered via the Engine::register_XXX API.

  • All the Rust functions defined in [packages] that are loaded into the [Engine].

In addition, during evaluation of an [AST], all script-defined functions bundled together within the [AST] are added to the global namespace and override any existing registered functions of the same names and number of parameters.

Anywhere in a Rhai script, when a function call is made, it is searched within the global namespace. Therefore, function calls in Rhai are late bound - meaning that the function called cannot be determined or guaranteed and there is no way to lock down the function being called. This aspect is very similar to JavaScript before ES6 modules.

// Compile a script into AST
let ast1 = engine.compile(
    r#"
        fn get_message() {
            "Hello!"                // greeting message
        }

        fn say_hello() {
            print(get_message());   // prints message
        }

        say_hello();
    "#
)?;

// Compile another script with an overriding function
let ast2 = engine.compile(r#"fn get_message() { "Boo!" }"#)?;

// Combine the two AST's
ast1 += ast2;                       // 'message' will be overwritten

engine.consume_ast(&ast1)?;         // prints 'Boo!'

Therefore, care must be taken when cross-calling functions to make sure that the correct functions are called.

The only practical way to ensure that a function is a correct one is to use [modules] - i.e. define the function in a separate module and then [import] it:

----------------
| message.rhai |
----------------

fn get_message() { "Hello!" }


---------------
| script.rhai |
---------------

import "message" as msg;

fn say_hello() {
    print(msg::get_message());
}
say_hello();