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/functions.md
2025-04-04 08:28:07 +02:00

5.0 KiB

Functions

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

Rhai supports defining functions in script via the fn keyword, with a syntax that is very similar to Rust without types.

Valid function names are the same as valid [variable] names.

fn add(x, y) {
    x + y
}

fn sub(x, y,) {     // trailing comma in parameters list is OK
    x - y
}

add(2, 3) == 5;

sub(2, 3,) == -1;   // trailing comma in arguments list is OK

Defining functions can be disabled via the [`no_function`] feature.

Use `is_def_fn` (not available under [`no_function`]) to detect if a Rhai function is defined
(and therefore callable) based on its name and the number of parameters (_arity_).

```rust
fn foo(x) { x + 1 }

is_def_fn("foo", 1) == true;

is_def_fn("foo", 0) == false;

is_def_fn("foo", 2) == false;

is_def_fn("bar", 1) == false;
```

Implicit Return

Just like in Rust, an implicit return can be used. In fact, the last statement of a block is always the block's return value regardless of whether it is terminated with a semicolon ;. This is different from Rust.

fn add(x, y) {      // implicit return:
    x + y;          // value of the last statement (no need for ending semicolon)
                    // is used as the return value
}

fn add2(x) {
    return x + 2;   // explicit return
}

add(2, 3) == 5;

add2(42) == 44;

Global Definitions Only

Functions can only be defined at the global level, never inside a block or another function.

Again, this is different from Rust.

// Global level is OK
fn add(x, y) {
    x + y
}

// The following will not compile
fn do_addition(x) {
    fn add_y(n) {   // <- syntax error:  cannot define inside another function
        n + y
    }

    add_y(x)
}

No Access to External Scope

Functions are not closures. They do not capture the calling environment and can only access their own parameters.

They cannot access [variables] external to the function itself.

let x = 42;

fn foo() {
    x               // <- error: variable 'x' not found
}

But Can Call Other Functions and Access Modules

All functions in the same [AST] can call each other.

fn foo(x) {         // function defined in the global namespace
    x + 1
}

fn bar(x) {
    foo(x)          // ok! function 'foo' can be called
}

In addition, [modules] [imported][import] at global level can be accessed.

import "hello" as hey;
import "world" as woo;

{
    import "x" as xyz;  // <- this module is not at global level
}                       // <- it goes away here

fn foo(x) {
    hey::process(x);    // ok! imported module 'hey' can be accessed

    print(woo::value);  // ok! imported module 'woo' can be accessed

    xyz::do_work();     // <- error: module 'xyz' not found
}

Automatic Global Module

When a [constant] is declared at global scope, it is added to a special [module] called [global].

Functions can access those [constants] via the special [global] [module].

Naturally, the automatic [global] [module] is not available under [no_function] nor [no_module].

const CONSTANT = 42;        // this constant is automatically added to 'global'

let hello = 1;              // variables are not added to 'global'

{
    const INNER = 0;        // this constant is not at global level
}                           // <- it goes away here

fn foo(x) {
    x * global::hello       // <- error: variable 'hello' not found in 'global'

    x * global::CONSTANT    // ok! 'CONSTANT' exists in 'global'

    x * global::INNER       // <- error: constant 'INNER' not found in 'global'
}

Use Before Definition Allowed

Unlike C/C++, functions in Rhai can be defined anywhere at global level.

A function does not need to be defined prior to being used in a script; a statement in the script can freely call a function defined afterwards.

This is similar to Rust and many other modern languages, such as JavaScript's function keyword.

let x = foo(41);    // <- I can do this!

fn foo(x) {         // <- define 'foo' after use
    x + 1
}

Arguments are Passed by Value

Functions defined in script always take [Dynamic] parameters (i.e. they can be of any types). Therefore, functions with the same name and same number of parameters are equivalent.

All arguments are passed by value, so all Rhai script-defined functions are pure (i.e. they never modify their arguments).

Any update to an argument will not be reflected back to the caller.

fn change(s) {      // 's' is passed by value
    s = 42;         // only a COPY of 's' is changed
}

let x = 500;

change(x);

x == 500;           // 'x' is NOT changed!

The only possibility for a Rhai script-defined function to modify an external variable is
via the [`this`](fn-method.md) pointer.