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/language/functions.md
2025-04-03 09:18:05 +02:00

223 lines
5.0 KiB
Markdown

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.
```rust
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
```
```admonish tip.small "Tip: Disable functions"
Defining functions can be disabled via the [`no_function`] feature.
```
~~~admonish tip.small "Tip: `is_def_fn`"
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.
```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.
```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.
```rust
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.
```rust
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.
```js
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`].
```rust
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.
```rust
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.
```rust
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!
```
```admonish warning.small "Rhai functions are pure"
The only possibility for a Rhai script-defined function to modify an external variable is
via the [`this`](fn-method.md) pointer.
```