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/rhai_engine/rhaibook/rust/modules/resolvers/file.md
2025-04-03 09:18:05 +02:00

5.0 KiB

FileModuleResolver

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


`FileModuleResolver` is the default for [`Engine::new`][`Engine`].

The default [module] resolution service, not available for [no_std] or [WASM] builds. Loads a script file (based off the current directory or a specified one) with .rhai extension.

Function Namespace

All functions in the [global namespace][function namespace], plus all those defined in the same [module], are merged into a unified [namespace][function namespace].

All [modules] imported at global level via [import] statements become sub-[modules], which are also available to [functions] defined within the same script file.

Base Directory


If the base directory is not set, then relative paths are based off the directory of the loading script.

This allows scripts to simply cross-load each other.

Relative paths are resolved relative to a root directory, which is usually the base directory.

The base directory can be set via FileModuleResolver::new_with_path or FileModuleResolver::set_base_path.

Custom [Scope]


This [`Scope`] can conveniently hold global [constants] etc.

The set_scope method adds an optional [Scope] which will be used to [optimize][script optimization] [module] scripts.

Caching


Use `enable_cache` to enable/disable the cache.

By default, [modules] are also cached so a script file is only evaluated once, even when repeatedly imported.

Unix Shebangs

On Unix-like systems, the shebang (#!) is used at the very beginning of a script file to mark a script with an interpreter (for Rhai this would be rhai-run).

If a script file starts with #!, the entire first line is skipped. Because of this, Rhai scripts with shebangs at the beginning need no special processing.

#!/home/to/me/bin/rhai-run

// This is a Rhai script

let answer = 42;
print(`The answer is: ${answer}`);

Example

┌────────────────┐
 my_module.rhai 
└────────────────┘

// This function overrides any in the main script.
private fn inner_message() { "hello! from module!" }

fn greet() {
    print(inner_message());     // call function in module script
}

fn greet_main() {
    print(main_message());      // call function not in module script
}


┌───────────┐
 main.rhai 
└───────────┘

// This function is overridden by the module script.
fn inner_message() { "hi! from main!" }

// This function is found by the module script.
fn main_message() { "main here!" }

import "my_module" as m;

m::greet();                     // prints "hello! from module!"

m::greet_main();                // prints "main here!"

Simulate Virtual Functions

When calling a namespace-qualified [function] defined within a [module], other [functions] defined within the same module override any similar-named [functions] (with the same number of parameters) defined in the [global namespace][function namespace].

This is to ensure that a [module] acts as a self-contained unit and [functions] defined in the calling script do not override [module] code.

In some situations, however, it is actually beneficial to do it in reverse: have [module] [functions] call [functions] defined in the calling script (i.e. in the [global namespace][function namespace]) if they exist, and only call those defined in the [module] if none are found.

One such situation is the need to provide a default implementation to a simulated virtual function:

┌────────────────┐
 my_module.rhai 
└────────────────┘

// Do not do this (it will override the main script):
// fn message() { "hello! from module!" }

// This function acts as the default implementation.
private fn default_message() { "hello! from module!" }

// This function depends on a 'virtual' function 'message'
// which is not defined in the module script.
fn greet() {
    if is_def_fn("message", 0) {    // 'is_def_fn' detects if 'message' is defined.
        print(message());
    } else {
        print(default_message());
    }
}


┌───────────┐
 main.rhai 
└───────────┘

// The main script defines 'message' which is needed by the module script.
fn message() { "hi! from main!" }

import "my_module" as m;

m::greet();                         // prints "hi! from main!"


┌────────────┐
 main2.rhai 
└────────────┘

// The main script does not define 'message' which is needed by the module script.

import "my_module" as m;

m::greet();                         // prints "hello! from module!"