This repository has been archived on 2025-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
rhaj/_archive/rhai_engine/rhaibook/language/fn-namespaces.md
2025-04-04 08:28:07 +02:00

5.1 KiB
Raw Blame History

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-matched 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].

Namespace Types

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

Namespace Quantity Source Lookup Sub-modules? Variables?
Global one
  1. [AST] being evaluated
  2. Engine::register_XXX API
  3. global registered [modules]
  4. [functions] in [imported][import] [modules] marked global
  5. [functions] in registered static [modules] marked global
simple name ignored ignored
Module many
  1. [Module] registered via Engine::register_static_module
  2. [Module] loaded via [import] statement
namespace-qualified name yes yes

Module Namespaces

There can be multiple [module] namespaces at any time during a script evaluation, usually loaded via the [import] statement.

Static [module] namespaces can also be registered into an [Engine] via Engine::register_static_module.

[Functions] and [variables] in module namespaces are isolated and encapsulated within their own environments.

They must be called or accessed in a namespace-qualified manner.

import "my_module" as m;        // new module namespace 'm' created via 'import'

let x = m::calc_result();       // namespace-qualified function call

let y = m::MY_NUMBER;           // namespace-qualified variable/constant access

let z = calc_result();          // <- error: function 'calc_result' not found
                                //    in global namespace!

Global Namespace

There is one global namespace for every [Engine], which includes (in the following search order):

  • all [functions] defined in the [AST] currently being evaluated,

  • all native Rust functions and iterators registered via the Engine::register_XXX API,

  • all functions and iterators defined in global [modules] that are registered into the [Engine] via register_global_module,

  • functions defined in [modules] registered into the [Engine] via register_static_module that are specifically marked for exposure to the global namespace (e.g. via the #[rhai(global)] attribute in a [plugin module]).

  • [functions] defined in [imported][import] [modules] that are specifically marked for exposure to the global namespace (e.g. via the #[rhai(global)] attribute in a [plugin module]).

Anywhere in a Rhai script, when a function call is made, the function is searched within the global namespace, in the above search order.

Therefore, function calls in Rhai are late bound meaning that the function called cannot be determined or guaranteed; 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.run_ast(&ast1)?;         // prints 'Boo!'

Therefore, care must be taken when cross-calling [functions] to make sure that the correct [function] is 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();