reorganize module
This commit is contained in:
		@@ -1,258 +0,0 @@
 | 
			
		||||
Arrays
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
Arrays are first-class citizens in Rhai.
 | 
			
		||||
 | 
			
		||||
All elements stored in an array are dynamic, and the array can freely grow or shrink with elements
 | 
			
		||||
added or removed.
 | 
			
		||||
 | 
			
		||||
[`type_of()`](type-of.md) an array returns `"array"`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Literal Syntax
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
Array literals are built within square brackets `[` ... `]` and separated by commas `,`:
 | 
			
		||||
 | 
			
		||||
> `[` _value_`,` _value_`,` ... `,` _value_ `]`
 | 
			
		||||
>
 | 
			
		||||
> `[` _value_`,` _value_`,` ... `,` _value_ `,` `]`     `// trailing comma is OK`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Element Access Syntax
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
### From beginning
 | 
			
		||||
 | 
			
		||||
Like C, arrays are accessed with zero-based, non-negative integer indices:
 | 
			
		||||
 | 
			
		||||
> _array_ `[` _index position from 0 to length−1_ `]`
 | 
			
		||||
 | 
			
		||||
### From end
 | 
			
		||||
 | 
			
		||||
A _negative_ position accesses an element in the array counting from the _end_, with −1 being the
 | 
			
		||||
_last_ element.
 | 
			
		||||
 | 
			
		||||
> _array_ `[` _index position from −1 to −length_ `]`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Built-in Functions
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
The following methods operate on arrays.
 | 
			
		||||
 | 
			
		||||
| Function                       | Parameter(s)                                                                                                                                                                     | Description                                                                                                                                                                                                                                                                                                     |
 | 
			
		||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `get`                          | position, counting from end if < 0                                                                                                                                               | gets a copy of the element at a certain position (`()` if the position is not valid)                                                                                                                                                                                                                            |
 | 
			
		||||
| `set`                          | <ol><li>position, counting from end if < 0</li><li>new element</li></ol>                                                                                                         | sets a certain position to a new value (no effect if the position is not valid)                                                                                                                                                                                                                                 |
 | 
			
		||||
| `push`, `+=` operator          | element to append (not an array)                                                                                                                                                 | appends an element to the end                                                                                                                                                                                                                                                                                   |
 | 
			
		||||
| `append`, `+=` operator        | array to append                                                                                                                                                                  | concatenates the second array to the end of the first                                                                                                                                                                                                                                                           |
 | 
			
		||||
| `+` operator                   | <ol><li>first array</li><li>second array</li></ol>                                                                                                                               | concatenates the first array with the second                                                                                                                                                                                                                                                                    |
 | 
			
		||||
| `==` operator                  | <ol><li>first array</li><li>second array</li></ol>                                                                                                                               | are two arrays the same (elements compared with the `==` operator, if defined)?                                                                                                                                                                                                                                 |
 | 
			
		||||
| `!=` operator                  | <ol><li>first array</li><li>second array</li></ol>                                                                                                                               | are two arrays different (elements compared with the `==` operator, if defined)?                                                                                                                                                                                                                                |
 | 
			
		||||
| `insert`                       | <ol><li>position, counting from end if < 0, end if ≥ length</li><li>element to insert</li></ol>                                                                                  | inserts an element at a certain position                                                                                                                                                                                                                                                                        |
 | 
			
		||||
| `pop`                          | _none_                                                                                                                                                                           | removes the last element and returns it (`()` if empty)                                                                                                                                                                                                                                                         |
 | 
			
		||||
| `shift`                        | _none_                                                                                                                                                                           | removes the first element and returns it (`()` if empty)                                                                                                                                                                                                                                                        |
 | 
			
		||||
| `extract`                      | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>_(optional)_ number of elements to extract, none if ≤ 0, to end if omitted</li></ol>                   | extracts a portion of the array into a new array                                                                                                                                                                                                                                                                |
 | 
			
		||||
| `extract`                      | [range](ranges.md) of elements to extract, from beginning if ≤ 0, to end if ≥ length                                                                                             | extracts a portion of the array into a new array                                                                                                                                                                                                                                                                |
 | 
			
		||||
| `remove`                       | position, counting from end if < 0                                                                                                                                               | removes an element at a particular position and returns it (`()` if the position is not valid)                                                                                                                                                                                                                  |
 | 
			
		||||
| `reverse`                      | _none_                                                                                                                                                                           | reverses the array                                                                                                                                                                                                                                                                                              |
 | 
			
		||||
| `len` method and property      | _none_                                                                                                                                                                           | returns the number of elements                                                                                                                                                                                                                                                                                  |
 | 
			
		||||
| `is_empty` method and property | _none_                                                                                                                                                                           | returns `true` if the array is empty                                                                                                                                                                                                                                                                            |
 | 
			
		||||
| `pad`                          | <ol><li>target length</li><li>element to pad</li></ol>                                                                                                                           | pads the array with an element to at least a specified length                                                                                                                                                                                                                                                   |
 | 
			
		||||
| `clear`                        | _none_                                                                                                                                                                           | empties the array                                                                                                                                                                                                                                                                                               |
 | 
			
		||||
| `truncate`                     | target length                                                                                                                                                                    | cuts off the array at exactly a specified length (discarding all subsequent elements)                                                                                                                                                                                                                           |
 | 
			
		||||
| `chop`                         | target length                                                                                                                                                                    | cuts off the head of the array, leaving the tail at exactly a specified length                                                                                                                                                                                                                                  |
 | 
			
		||||
| `split`                        | <ol><li>array</li><li>position to split at, counting from end if < 0, end if ≥ length</li></ol>                                                                                  | splits the array into two arrays, starting from a specified position                                                                                                                                                                                                                                            |
 | 
			
		||||
| `for_each`                     | [function pointer](fn-ptr.md) for processing elements                                                                                                                            | run through each element in the array in order, binding each to `this` and calling the processing function taking the following parameters: <ol><li>`this`: array element</li><li>_(optional)_ index position</li></ol>                                                                                         |
 | 
			
		||||
| `drain`                        | [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md))                                                                                                  | removes all elements (returning them) that return `true` when called with the predicate function taking the following parameters (if none, the array element is bound to `this`):<ol><li>array element</li><li>_(optional)_ index position</li></ol>                                                            |
 | 
			
		||||
| `drain`                        | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of elements to remove, none if ≤ 0</li></ol>                                                    | removes a portion of the array, returning the removed elements as a new array                                                                                                                                                                                                                                   |
 | 
			
		||||
| `drain`                        | [range](ranges.md) of elements to remove, from beginning if ≤ 0, to end if ≥ length                                                                                              | removes a portion of the array, returning the removed elements as a new array                                                                                                                                                                                                                                   |
 | 
			
		||||
| `retain`                       | [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md))                                                                                                  | removes all elements (returning them) that do not return `true` when called with the predicate function taking the following parameters (if none, the array element is bound to `this`):<ol><li>array element</li><li>_(optional)_ index position</li></ol>                                                     |
 | 
			
		||||
| `retain`                       | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of elements to retain, none if ≤ 0</li></ol>                                                    | retains a portion of the array, removes all other elements and returning them as a new array                                                                                                                                                                                                                    |
 | 
			
		||||
| `retain`                       | [range](ranges.md) of elements to retain, from beginning if ≤ 0, to end if ≥ length                                                                                              | retains a portion of the array, removes all other bytes and returning them as a new array                                                                                                                                                                                                                       |
 | 
			
		||||
| `splice`                       | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of elements to remove, none if ≤ 0</li><li>array to insert</li></ol>                            | replaces a portion of the array with another (not necessarily of the same length as the replaced portion)                                                                                                                                                                                                       |
 | 
			
		||||
| `splice`                       | <ol><li>[range](ranges.md) of elements to remove, from beginning if ≤ 0, to end if ≥ length</li><li>array to insert</li></ol>                                                    | replaces a portion of the array with another (not necessarily of the same length as the replaced portion)                                                                                                                                                                                                       |
 | 
			
		||||
| `filter`                       | [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md))                                                                                                  | constructs a new array with all elements that return `true` when called with the predicate function taking the following parameters (if none, the array element is bound to `this`):<ol><li>array element</li><li>_(optional)_ index position</li></ol>                                                         |
 | 
			
		||||
| `contains`, `in` operator      | element to find                                                                                                                                                                  | does the array contain an element? The `==` operator is used for comparison                                                                                                                                                                                                                                     |
 | 
			
		||||
| `index_of`                     | <ol><li>element to find (not a [function pointer](fn-ptr.md))</li><li>_(optional)_ start position, counting from end if < 0, end if ≥ length</li></ol>                           | returns the position of the first element in the array that equals the supplied element (using the `==` operator, if defined), or −1 if not found</li></ol>                                                                                                                                                     |
 | 
			
		||||
| `index_of`                     | <ol><li>[function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md))</li><li>_(optional)_ start position, counting from end if < 0, end if ≥ length</li></ol> | returns the position of the first element in the array that returns `true` when called with the predicate function, or −1 if not found:<ol><li>`this`: array element</li><li>_(optional)_ index position</li></ol>                                                                                              |
 | 
			
		||||
| `find`                         | <ol><li>[function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md))</li><li>_(optional)_ start position, counting from end if < 0, end if ≥ length</li></ol> | returns the first element in the array that returns `true` when called with the predicate function, or `()` if not found:<ol><li>array element (if none, the array element is bound to `this`)</li><li>_(optional)_ index position</li></ol>                                                                    |
 | 
			
		||||
| `find_map`                     | <ol><li>[function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md))</li><li>_(optional)_ start position, counting from end if < 0, end if ≥ length</li></ol> | returns the first non-`()` value of the first element in the array when called with the predicate function, or `()` if not found:<ol><li>array element (if none, the array element is bound to `this`)</li><li>_(optional)_ index position</li></ol>                                                            |
 | 
			
		||||
| `dedup`                        | _(optional)_ [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md)); if omitted, the `==` operator is used, if defined                                  | removes all but the first of _consecutive_ elements in the array that return `true` when called with the predicate function (non-consecutive duplicates are _not_ removed):<br/>1st & 2nd parameters: two elements in the array                                                                                 |
 | 
			
		||||
| `map`                          | [function pointer](fn-ptr.md) to conversion function (usually a [closure](fn-closure.md))                                                                                        | constructs a new array with all elements mapped to the result of applying the conversion function taking the following parameters (if none, the array element is bound to `this`):<ol><li>array element</li><li>_(optional)_ index position</li></ol>                                                           |
 | 
			
		||||
| `reduce`                       | <ol><li>[function pointer](fn-ptr.md) to accumulator function (usually a [closure](fn-closure.md))</li><li>_(optional)_ the initial value</li></ol>                              | reduces the array into a single value via the accumulator function taking the following parameters (if the second parameter is omitted, the array element is bound to `this`):<ol><li>accumulated value (`()` initially)</li><li>array element</li><li>_(optional)_ index position</li></ol>                    |
 | 
			
		||||
| `reduce_rev`                   | <ol><li>[function pointer](fn-ptr.md) to accumulator function (usually a [closure](fn-closure.md))</li><li>_(optional)_ the initial value</li></ol>                              | reduces the array (in reverse order) into a single value via the accumulator function taking the following parameters (if the second parameter is omitted, the array element is bound to `this`):<ol><li>accumulated value (`()` initially)</li><li>array element</li><li>_(optional)_ index position</li></ol> |
 | 
			
		||||
| `some`                         | [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md))                                                                                                  | returns `true` if any element returns `true` when called with the predicate function taking the following parameters (if none, the array element is bound to `this`):<ol><li>array element</li><li>_(optional)_ index position</li></ol>                                                                        |
 | 
			
		||||
| `all`                          | [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md))                                                                                                  | returns `true` if all elements return `true` when called with the predicate function taking the following parameters (if none, the array element is bound to `this`):<ol><li>array element</li><li>_(optional)_ index position</li></ol>                                                                        |
 | 
			
		||||
| `sort`                         | [function pointer](fn-ptr.md) to a comparison function (usually a [closure](fn-closure.md))                                                                                      | sorts the array with a comparison function taking the following parameters:<ol><li>first element</li><li>second element<br/>return value: `INT` < 0 if first < second, > 0 if first > second, 0 if first == second</li></ol>                                                                                    |
 | 
			
		||||
| `sort`                         | _none_                                                                                                                                                                           | sorts a _homogeneous_ array containing only elements of the same comparable built-in type (integers, floating-point, decimal, [string](strings-chars.md), [character](strings-chars.md), `bool`, `()`)                                                                                                          |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let y = [2, 3];             // y == [2, 3]
 | 
			
		||||
 | 
			
		||||
let y = [2, 3,];            // y == [2, 3]
 | 
			
		||||
 | 
			
		||||
y.insert(0, 1);             // y == [1, 2, 3]
 | 
			
		||||
 | 
			
		||||
y.insert(999, 4);           // y == [1, 2, 3, 4]
 | 
			
		||||
 | 
			
		||||
y.len == 4;
 | 
			
		||||
 | 
			
		||||
y[0] == 1;
 | 
			
		||||
y[1] == 2;
 | 
			
		||||
y[2] == 3;
 | 
			
		||||
y[3] == 4;
 | 
			
		||||
 | 
			
		||||
(1 in y) == true;           // use 'in' to test if an element exists in the array
 | 
			
		||||
 | 
			
		||||
(42 in y) == false;         // 'in' uses the 'contains' function, which uses the
 | 
			
		||||
                            // '==' operator (that users can override)
 | 
			
		||||
                            // to check if the target element exists in the array
 | 
			
		||||
 | 
			
		||||
y.contains(1) == true;      // the above de-sugars to this
 | 
			
		||||
 | 
			
		||||
y[1] = 42;                  // y == [1, 42, 3, 4]
 | 
			
		||||
 | 
			
		||||
(42 in y) == true;
 | 
			
		||||
 | 
			
		||||
y.remove(2) == 3;           // y == [1, 42, 4]
 | 
			
		||||
 | 
			
		||||
y.len == 3;
 | 
			
		||||
 | 
			
		||||
y[2] == 4;                  // elements after the removed element are shifted
 | 
			
		||||
 | 
			
		||||
ts.list = y;                // arrays can be assigned completely (by value copy)
 | 
			
		||||
 | 
			
		||||
ts.list[1] == 42;
 | 
			
		||||
 | 
			
		||||
[1, 2, 3][0] == 1;          // indexing on array literal
 | 
			
		||||
 | 
			
		||||
[1, 2, 3][-1] == 3;         // negative position counts from the end
 | 
			
		||||
 | 
			
		||||
fn abc() {
 | 
			
		||||
    [42, 43, 44]            // a function returning an array
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
abc()[0] == 42;
 | 
			
		||||
 | 
			
		||||
y.push(4);                  // y == [1, 42, 4, 4]
 | 
			
		||||
 | 
			
		||||
y += 5;                     // y == [1, 42, 4, 4, 5]
 | 
			
		||||
 | 
			
		||||
y.len == 5;
 | 
			
		||||
 | 
			
		||||
y.shift() == 1;             // y == [42, 4, 4, 5]
 | 
			
		||||
 | 
			
		||||
y.chop(3);                  // y == [4, 4, 5]
 | 
			
		||||
 | 
			
		||||
y.len == 3;
 | 
			
		||||
 | 
			
		||||
y.pop() == 5;               // y == [4, 4]
 | 
			
		||||
 | 
			
		||||
y.len == 2;
 | 
			
		||||
 | 
			
		||||
for element in y {          // arrays can be iterated with a 'for' statement
 | 
			
		||||
    print(element);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
y.pad(6, "hello");          // y == [4, 4, "hello", "hello", "hello", "hello"]
 | 
			
		||||
 | 
			
		||||
y.len == 6;
 | 
			
		||||
 | 
			
		||||
y.truncate(4);              // y == [4, 4, "hello", "hello"]
 | 
			
		||||
 | 
			
		||||
y.len == 4;
 | 
			
		||||
 | 
			
		||||
y.clear();                  // y == []
 | 
			
		||||
 | 
			
		||||
y.len == 0;
 | 
			
		||||
 | 
			
		||||
// The examples below use 'a' as the master array
 | 
			
		||||
 | 
			
		||||
let a = [42, 123, 99];
 | 
			
		||||
 | 
			
		||||
a.map(|v| v + 1);           // returns [43, 124, 100]
 | 
			
		||||
 | 
			
		||||
a.map(|| this + 1);         // returns [43, 124, 100]
 | 
			
		||||
 | 
			
		||||
a.map(|v, i| v + i);        // returns [42, 124, 101]
 | 
			
		||||
 | 
			
		||||
a.filter(|v| v > 50);       // returns [123, 99]
 | 
			
		||||
 | 
			
		||||
a.filter(|| this > 50);     // returns [123, 99]
 | 
			
		||||
 | 
			
		||||
a.filter(|v, i| i == 1);    // returns [123]
 | 
			
		||||
 | 
			
		||||
a.filter("is_odd");         // returns [123, 99]
 | 
			
		||||
 | 
			
		||||
a.filter(Fn("is_odd"));     // <- previous statement is equivalent to this...
 | 
			
		||||
 | 
			
		||||
a.filter(|v| is_odd(v));    // <- or this
 | 
			
		||||
 | 
			
		||||
a.some(|v| v > 50);         // returns true
 | 
			
		||||
 | 
			
		||||
a.some(|| this > 50);       // returns true
 | 
			
		||||
 | 
			
		||||
a.some(|v, i| v < i);       // returns false
 | 
			
		||||
 | 
			
		||||
a.all(|v| v > 50);          // returns false
 | 
			
		||||
 | 
			
		||||
a.all(|| this > 50);        // returns false
 | 
			
		||||
 | 
			
		||||
a.all(|v, i| v > i);        // returns true
 | 
			
		||||
 | 
			
		||||
// Reducing - initial value provided directly
 | 
			
		||||
a.reduce(|sum| sum + this, 0) == 264;
 | 
			
		||||
 | 
			
		||||
// Reducing - initial value provided directly
 | 
			
		||||
a.reduce(|sum, v| sum + v, 0) == 264;
 | 
			
		||||
 | 
			
		||||
// Reducing - initial value is '()'
 | 
			
		||||
a.reduce(
 | 
			
		||||
    |sum, v| if sum.type_of() == "()" { v } else { sum + v }
 | 
			
		||||
) == 264;
 | 
			
		||||
 | 
			
		||||
// Reducing - initial value has index position == 0
 | 
			
		||||
a.reduce(|sum, v, i|
 | 
			
		||||
    if i == 0 { v } else { sum + v }
 | 
			
		||||
) == 264;
 | 
			
		||||
 | 
			
		||||
// Reducing in reverse - initial value provided directly
 | 
			
		||||
a.reduce_rev(|sum| sum + this, 0) == 264;
 | 
			
		||||
 | 
			
		||||
// Reducing in reverse - initial value provided directly
 | 
			
		||||
a.reduce_rev(|sum, v| sum + v, 0) == 264;
 | 
			
		||||
 | 
			
		||||
// Reducing in reverse - initial value is '()'
 | 
			
		||||
a.reduce_rev(
 | 
			
		||||
    |sum, v| if sum.type_of() == "()" { v } else { sum + v }
 | 
			
		||||
) == 264;
 | 
			
		||||
 | 
			
		||||
// Reducing in reverse - initial value has index position == 0
 | 
			
		||||
a.reduce_rev(|sum, v, i|
 | 
			
		||||
    if i == 2 { v } else { sum + v }
 | 
			
		||||
) == 264;
 | 
			
		||||
 | 
			
		||||
// In-place modification
 | 
			
		||||
 | 
			
		||||
a.splice(1..=1, [1, 3, 2]); // a == [42, 1, 3, 2, 99]
 | 
			
		||||
 | 
			
		||||
a.extract(1..=3);           // returns [1, 3, 2]
 | 
			
		||||
 | 
			
		||||
a.sort(|x, y| y - x);       // a == [99, 42, 3, 2, 1]
 | 
			
		||||
 | 
			
		||||
a.sort();                   // a == [1, 2, 3, 42, 99]
 | 
			
		||||
 | 
			
		||||
a.drain(|v| v <= 1);        // a == [2, 3, 42, 99]
 | 
			
		||||
 | 
			
		||||
a.drain(|v, i| i ≥ 3);      // a == [2, 3, 42]
 | 
			
		||||
 | 
			
		||||
a.retain(|v| v > 10);       // a == [42]
 | 
			
		||||
 | 
			
		||||
a.retain(|v, i| i > 0);     // a == []
 | 
			
		||||
```
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
Compound Assignments
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
Compound assignments are [assignments](assignments.md) with a [binary operator](operators.md) attached.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
number += 8;            // number = number + 8
 | 
			
		||||
 | 
			
		||||
number -= 7;            // number = number - 7
 | 
			
		||||
 | 
			
		||||
number *= 6;            // number = number * 6
 | 
			
		||||
 | 
			
		||||
number /= 5;            // number = number / 5
 | 
			
		||||
 | 
			
		||||
number %= 4;            // number = number % 4
 | 
			
		||||
 | 
			
		||||
number **= 3;           // number = number ** 3
 | 
			
		||||
 | 
			
		||||
number <<= 2;           // number = number << 2
 | 
			
		||||
 | 
			
		||||
number >>= 1;           // number = number >> 1
 | 
			
		||||
 | 
			
		||||
number &= 0x00ff;       // number = number & 0x00ff;
 | 
			
		||||
 | 
			
		||||
number |= 0x00ff;       // number = number | 0x00ff;
 | 
			
		||||
 | 
			
		||||
number ^= 0x00ff;       // number = number ^ 0x00ff;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The Flexible `+=`
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
The the `+` and `+=` operators are often [overloaded](overload.md) to perform build-up
 | 
			
		||||
operations for different data types.
 | 
			
		||||
 | 
			
		||||
### Build strings
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let my_str = "abc";
 | 
			
		||||
 | 
			
		||||
my_str += "ABC";
 | 
			
		||||
my_str += 12345;
 | 
			
		||||
 | 
			
		||||
my_str == "abcABC12345"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Concatenate arrays
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let my_array = [1, 2, 3];
 | 
			
		||||
 | 
			
		||||
my_array += [4, 5];
 | 
			
		||||
 | 
			
		||||
my_array == [1, 2, 3, 4, 5];
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Concatenate BLOB's
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let my_blob = blob(3, 0x42);
 | 
			
		||||
 | 
			
		||||
my_blob += blob(5, 0x89);
 | 
			
		||||
 | 
			
		||||
my_blob.to_string() == "[4242428989898989]";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Mix two object maps together
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let my_obj = #{ a:1, b:2 };
 | 
			
		||||
 | 
			
		||||
my_obj += #{ c:3, d:4, e:5 };
 | 
			
		||||
 | 
			
		||||
my_obj == #{ a:1, b:2, c:3, d:4, e:5 };
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Add seconds to timestamps
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let now = timestamp();
 | 
			
		||||
 | 
			
		||||
now += 42.0;
 | 
			
		||||
 | 
			
		||||
(now - timestamp()).round() == 42.0;
 | 
			
		||||
```
 | 
			
		||||
@@ -1,121 +0,0 @@
 | 
			
		||||
Assignments
 | 
			
		||||
===========
 | 
			
		||||
 | 
			
		||||
Value assignments to [variables](variables.md) use the `=` symbol.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let foo = 42;
 | 
			
		||||
 | 
			
		||||
bar = 123 * 456 - 789;
 | 
			
		||||
 | 
			
		||||
x[1][2].prop = do_calculation();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Valid Assignment Targets
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
The left-hand-side (LHS) of an assignment statement must be a valid
 | 
			
		||||
_[l-value](https://en.wikipedia.org/wiki/Value_(computer_science))_, which must be rooted in a
 | 
			
		||||
[variable](variables.md), potentially extended via indexing or properties.
 | 
			
		||||
 | 
			
		||||
~~~admonish bug "Assigning to invalid l-value"
 | 
			
		||||
 | 
			
		||||
Expressions that are not valid _l-values_ cannot be assigned to.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
x = 42;                 // variable is an l-value
 | 
			
		||||
 | 
			
		||||
x[1][2][3] = 42         // variable indexing is an l-value
 | 
			
		||||
 | 
			
		||||
x.prop1.prop2 = 42;     // variable property is an l-value
 | 
			
		||||
 | 
			
		||||
foo(x) = 42;            // syntax error: function call is not an l-value
 | 
			
		||||
 | 
			
		||||
x.foo() = 42;           // syntax error: method call is not an l-value
 | 
			
		||||
 | 
			
		||||
(x + y) = 42;           // syntax error: binary op is not an l-value
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
Values are Cloned
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
Values assigned are always _cloned_.
 | 
			
		||||
So care must be taken when assigning large data types (such as [arrays](arrays.md)).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
x = y;                  // value of 'y' is cloned
 | 
			
		||||
 | 
			
		||||
x == y;                 // both 'x' and 'y' hold different copies
 | 
			
		||||
                        // of the same value
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Moving Data
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
When assigning large data types, sometimes it is desirable to _move_ the data instead of cloning it.
 | 
			
		||||
 | 
			
		||||
Use the `take` function to _move_ data.
 | 
			
		||||
 | 
			
		||||
### The original variable is left with `()`
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
x = take(y);            // value of 'y' is moved to 'x'
 | 
			
		||||
 | 
			
		||||
y == ();                // 'y' now holds '()'
 | 
			
		||||
 | 
			
		||||
x != y;                 // 'x' holds the original value of 'y'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Return large data types from functions
 | 
			
		||||
 | 
			
		||||
`take` is convenient when returning large data types from a [function](functions.md).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn get_large_value_naive() {
 | 
			
		||||
    let large_result = do_complex_calculation();
 | 
			
		||||
 | 
			
		||||
    large_result.done = true;
 | 
			
		||||
 | 
			
		||||
    // Return a cloned copy of the result, then the
 | 
			
		||||
    // local variable 'large_result' is thrown away!
 | 
			
		||||
    large_result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_large_value_smart() {
 | 
			
		||||
    let large_result = do_complex_calculation();
 | 
			
		||||
 | 
			
		||||
    large_result.done = true;
 | 
			
		||||
 | 
			
		||||
    // Return the result without cloning!
 | 
			
		||||
    // Method style call is also OK.
 | 
			
		||||
    large_result.take()
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Assigning large data types to object map properties
 | 
			
		||||
 | 
			
		||||
`take` is useful when assigning large data types to [object map](object-maps.md) properties.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = [];
 | 
			
		||||
 | 
			
		||||
// Build a large array
 | 
			
		||||
for n in 0..1000000 { x += n; }
 | 
			
		||||
 | 
			
		||||
// The following clones the large array from 'x'.
 | 
			
		||||
// Both 'my_object.my_property' and 'x' now hold exact copies
 | 
			
		||||
// of the same large array!
 | 
			
		||||
my_object.my_property = x;
 | 
			
		||||
 | 
			
		||||
// Move it to object map property via 'take' without cloning.
 | 
			
		||||
// 'x' now holds '()'.
 | 
			
		||||
my_object.my_property = x.take();
 | 
			
		||||
 | 
			
		||||
// Without 'take', the following must be done to avoid cloning:
 | 
			
		||||
my_object.my_property = [];
 | 
			
		||||
 | 
			
		||||
for n in 0..1000000 { my_object.my_property += n; }
 | 
			
		||||
```
 | 
			
		||||
@@ -1,123 +0,0 @@
 | 
			
		||||
Integer as Bit-Fields
 | 
			
		||||
=====================
 | 
			
		||||
 | 
			
		||||
```admonish note.side
 | 
			
		||||
 | 
			
		||||
Nothing here cannot be done via standard bit-manipulation (i.e. shifting and masking).
 | 
			
		||||
 | 
			
		||||
Built-in support is more elegant and performant since it usually replaces a sequence of multiple steps.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Since bit-wise operators are defined on integer numbers, individual bits can also be accessed and
 | 
			
		||||
manipulated via an indexing syntax.
 | 
			
		||||
 | 
			
		||||
If a bit is set (i.e. `1`), the index access returns `true`.
 | 
			
		||||
 | 
			
		||||
If a bit is not set (i.e. `0`), the index access returns `false`.
 | 
			
		||||
 | 
			
		||||
When a [range](ranges.md) is used, the bits within the [range](ranges.md) are shifted and extracted
 | 
			
		||||
as an integer value.
 | 
			
		||||
 | 
			
		||||
Bit-fields are very commonly used in embedded systems which must squeeze data into limited memory.
 | 
			
		||||
 | 
			
		||||
Built-in support makes handling them efficient.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Syntax
 | 
			
		||||
------
 | 
			
		||||
 | 
			
		||||
### From Least-Significant Bit (LSB)
 | 
			
		||||
 | 
			
		||||
Bits in a bit-field are accessed with zero-based, non-negative integer indices:
 | 
			
		||||
 | 
			
		||||
> _integer_ `[` _index from 0 to 63 or 31_ `]`
 | 
			
		||||
>
 | 
			
		||||
> _integer_ `[` _index from 0 to 63 or 31_ `] =` `true` or `false` ;
 | 
			
		||||
 | 
			
		||||
[Ranges] can also be used:
 | 
			
		||||
 | 
			
		||||
> _integer_ `[` _start_ `..` _end_ `]`  
 | 
			
		||||
> _integer_ `[` _start_ `..=` _end_ `]`
 | 
			
		||||
>
 | 
			
		||||
> _integer_ `[` _start_ `..` _end_ `] =` _new integer value_ ;  
 | 
			
		||||
> _integer_ `[` _start_ `..=` _end_ `] =` _new integer value_ ;
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "Number of bits"
 | 
			
		||||
 | 
			
		||||
The maximum bit number that can be accessed is one less than the number of bits for the
 | 
			
		||||
system integer type (usually 63).
 | 
			
		||||
 | 
			
		||||
Bits outside of the range are ignored.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### From Most-Significant Bit (MSB)
 | 
			
		||||
 | 
			
		||||
A _negative_ index accesses a bit in the bit-field counting from the _end_, or from the
 | 
			
		||||
_most-significant bit_, with −1 being the _highest_ bit.
 | 
			
		||||
 | 
			
		||||
> _integer_ `[` _index from −1 to −64 or −32_ `]`
 | 
			
		||||
>
 | 
			
		||||
> _integer_ `[` _index from −1 to −64 or −32_ `] =` `true` or `false` ;
 | 
			
		||||
 | 
			
		||||
[Ranges](ranges.md) always count from the least-significant bit (LSB) and has no support for
 | 
			
		||||
negative positions.
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "Number of bits"
 | 
			
		||||
 | 
			
		||||
The maximum bit number that can be accessed is negative the number of bits for the
 | 
			
		||||
system integer type (usually −64).
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Bit-Field Functions
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
The following standard functions operate on bit-fields.
 | 
			
		||||
 | 
			
		||||
| Function                   | Parameter(s)                                                                                                                                                   | Description                                               |
 | 
			
		||||
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
 | 
			
		||||
| `get_bit`                  | bit number, counting from MSB if < 0                                                                                                                           | returns the state of a bit: `true` if `1`, `false` if `0` |
 | 
			
		||||
| `set_bit`                  | <ol><li>bit number, counting from MSB if < 0</li><li>new state: `true` if `1`, `false` if `0`</li></ol>                                                        | sets the state of a bit                                   |
 | 
			
		||||
| `get_bits`                 | <ol><li>starting bit number, counting from MSB if < 0</li><li>number of bits to extract, none if < 1, to MSB if ≥ _length_</li></ol>                           | extracts a number of bits, shifted towards LSB            |
 | 
			
		||||
| `get_bits`                 | [range](ranges.md) of bits                                                                                                                                     | extracts a number of bits, shifted towards LSB            |
 | 
			
		||||
| `set_bits`                 | <ol><li>starting bit number, counting from MSB if < 0</li><li>number of bits to set, none if < 1, to MSB if ≥ _length_<br/>3) new value</li></ol>              | sets a number of bits from the new value                  |
 | 
			
		||||
| `set_bits`                 | <ol><li>[range](ranges.md) of bits</li><li>new value</li></ol>                                                                                                 | sets a number of bits from the new value                  |
 | 
			
		||||
| `bits` method and property | <ol><li>_(optional)_ starting bit number, counting from MSB if < 0</li><li>_(optional)_ number of bits to extract, none if < 1, to MSB if ≥ _length_</li></ol> | allows iteration over the bits of a bit-field             |
 | 
			
		||||
| `bits`                     | [range](ranges.md) of bits                                                                                                                                     | allows iteration over the bits of a bit-field             |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```js , no_run
 | 
			
		||||
// Assume the following bits fields in a single 16-bit word:
 | 
			
		||||
// ┌─────────┬────────────┬──────┬─────────┐
 | 
			
		||||
// │  15-12  │    11-4    │  3   │   2-0   │
 | 
			
		||||
// ├─────────┼────────────┼──────┼─────────┤
 | 
			
		||||
// │    0    │ 0-255 data │ flag │ command │
 | 
			
		||||
// └─────────┴────────────┴──────┴─────────┘
 | 
			
		||||
 | 
			
		||||
let value = read_start_hw_register(42);
 | 
			
		||||
 | 
			
		||||
let command = value.get_bits(0, 3);         // Command = bits 0-2
 | 
			
		||||
 | 
			
		||||
let flag = value[3];                        // Flag = bit 3
 | 
			
		||||
 | 
			
		||||
let data = value[4..=11];                   // Data = bits 4-11
 | 
			
		||||
let data = value.get_bits(4..=11);          // <- above is the same as this
 | 
			
		||||
 | 
			
		||||
let reserved = value.get_bits(-4);          // Reserved = last 4 bits
 | 
			
		||||
 | 
			
		||||
if reserved != 0 {
 | 
			
		||||
    throw reserved;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
switch command {
 | 
			
		||||
    0 => print(`Data = ${data}`),
 | 
			
		||||
    1 => value[4..=11] = data / 2,
 | 
			
		||||
    2 => value[3] = !flag,
 | 
			
		||||
    _ => print(`Unknown: ${command}`)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,192 +0,0 @@
 | 
			
		||||
BLOB's
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
BLOB's (**B**inary **L**arge **OB**jects), used to hold packed arrays of bytes, have built-in
 | 
			
		||||
support in Rhai.
 | 
			
		||||
 | 
			
		||||
A BLOB has no literal representation, but is created via the `blob` function, or simply returned as
 | 
			
		||||
the result of a function call (e.g. `generate_thumbnail_image` that generates a thumbnail version of
 | 
			
		||||
a large image as a BLOB).
 | 
			
		||||
 | 
			
		||||
All items stored in a BLOB are bytes (i.e. `u8`) and the BLOB can freely grow or shrink with bytes
 | 
			
		||||
added or removed.
 | 
			
		||||
 | 
			
		||||
[`type_of()`](type-of.md) a BLOB returns `"blob"`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Element Access Syntax
 | 
			
		||||
---------------------
 | 
			
		||||
 | 
			
		||||
### From beginning
 | 
			
		||||
 | 
			
		||||
Like [arrays](arrays.md), BLOB's are accessed with zero-based, non-negative integer indices:
 | 
			
		||||
 | 
			
		||||
> _blob_ `[` _index position from 0 to length−1_ `]`
 | 
			
		||||
 | 
			
		||||
### From end
 | 
			
		||||
 | 
			
		||||
A _negative_ position accesses an element in the BLOB counting from the _end_, with −1 being the
 | 
			
		||||
_last_ element.
 | 
			
		||||
 | 
			
		||||
> _blob_ `[` _index position from −1 to −length_ `]`
 | 
			
		||||
 | 
			
		||||
```admonish info.small "Byte values"
 | 
			
		||||
 | 
			
		||||
The value of a particular byte in a BLOB is mapped to an integer.
 | 
			
		||||
 | 
			
		||||
Only the lowest 8 bits are significant, all other bits are ignored.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Create a BLOB
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
The function `blob` allows creating an empty BLOB, optionally filling it to a required size with a
 | 
			
		||||
particular value (default zero).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = blob();             // empty BLOB
 | 
			
		||||
 | 
			
		||||
let x = blob(10);           // BLOB with ten zeros
 | 
			
		||||
 | 
			
		||||
let x = blob(50, 42);       // BLOB with 50x 42's
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip "Tip: Initialize with byte stream"
 | 
			
		||||
 | 
			
		||||
To quickly initialize a BLOB with a particular byte stream, the `write_be` method can be used to
 | 
			
		||||
write eight bytes at a time (four under 32-bit) in big-endian byte order.
 | 
			
		||||
 | 
			
		||||
If fewer than eight bytes are needed, remember to right-pad the number as big-endian byte order is used.
 | 
			
		||||
 | 
			
		||||
~~~rust
 | 
			
		||||
let buf = blob(12, 0);      // BLOB with 12x zeros
 | 
			
		||||
 | 
			
		||||
// Write eight bytes at a time, in big-endian order
 | 
			
		||||
buf.write_be(0, 8, 0xab_cd_ef_12_34_56_78_90);
 | 
			
		||||
buf.write_be(8, 8, 0x0a_0b_0c_0d_00_00_00_00);
 | 
			
		||||
                            //   ^^^^^^^^^^^ remember to pad unused bytes
 | 
			
		||||
 | 
			
		||||
print(buf);                 // prints "[abcdef1234567890 0a0b0c0d]"
 | 
			
		||||
 | 
			
		||||
buf[3] == 0x12;
 | 
			
		||||
buf[10] == 0x0c;
 | 
			
		||||
 | 
			
		||||
// Under 'only_i32', write four bytes at a time:
 | 
			
		||||
buf.write_be(0, 4, 0xab_cd_ef_12);
 | 
			
		||||
buf.write_be(4, 4, 0x34_56_78_90);
 | 
			
		||||
buf.write_be(8, 4, 0x0a_0b_0c_0d);
 | 
			
		||||
~~~
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Writing ASCII Bytes
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
```admonish warning.side "Non-ASCII"
 | 
			
		||||
 | 
			
		||||
Non-ASCII characters (i.e. characters not within 1-127) are ignored.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For many embedded applications, it is necessary to encode an ASCII [string](strings-chars.md) as a
 | 
			
		||||
byte stream.
 | 
			
		||||
 | 
			
		||||
Use the `write_ascii` method to write ASCII [strings](strings-chars.md) into any specific
 | 
			
		||||
[range](ranges.md) within a BLOB.
 | 
			
		||||
 | 
			
		||||
The following is an example of a building a 16-byte command to send to an embedded device.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Assume the following 16-byte command for an embedded device:
 | 
			
		||||
// ┌─────────┬───────────────┬──────────────────────────────────┬───────┐
 | 
			
		||||
// │    0    │       1       │              2-13                │ 14-15 │
 | 
			
		||||
// ├─────────┼───────────────┼──────────────────────────────────┼───────┤
 | 
			
		||||
// │ command │ string length │ ASCII string, max. 12 characters │  CRC  │
 | 
			
		||||
// └─────────┴───────────────┴──────────────────────────────────┴───────┘
 | 
			
		||||
 | 
			
		||||
let buf = blob(16, 0);      // initialize command buffer
 | 
			
		||||
 | 
			
		||||
let text = "foo & bar";     // text string to send to device
 | 
			
		||||
 | 
			
		||||
buf[0] = 0x42;              // command code
 | 
			
		||||
buf[1] = s.len();           // length of string
 | 
			
		||||
 | 
			
		||||
buf.write_ascii(2..14, text);   // write the string
 | 
			
		||||
 | 
			
		||||
let crc = buf.calc_crc();   // calculate CRC
 | 
			
		||||
 | 
			
		||||
buf.write_le(14, 2, crc);   // write CRC
 | 
			
		||||
 | 
			
		||||
print(buf);                 // prints "[4209666f6f202620 626172000000abcd]"
 | 
			
		||||
                            //          ^^ command code              ^^^^ CRC
 | 
			
		||||
                            //            ^^ string length
 | 
			
		||||
                            //              ^^^^^^^^^^^^^^^^^^^ foo & bar
 | 
			
		||||
 | 
			
		||||
device.send(buf);           // send command to device
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish question.small "What if I need UTF-8?"
 | 
			
		||||
 | 
			
		||||
The `write_utf8` function writes a string in UTF-8 encoding.
 | 
			
		||||
 | 
			
		||||
UTF-8, however, is not very common for embedded applications.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Built-in Functions
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
The following functions operate on BLOB's.
 | 
			
		||||
 | 
			
		||||
| Functions                       | Parameter(s)                                                                                                                                                                                                       | Description                                                                                                                                          |
 | 
			
		||||
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `blob` constructor function     | <ol><li>_(optional)_ initial length of the BLOB</li><li>_(optional)_ initial byte value</li></ol>                                                                                                                  | creates a new BLOB, optionally of a particular length filled with an initial byte value (default = 0)                                                |
 | 
			
		||||
| `to_array`                      | _none_                                                                                                                                                                                                             | converts the BLOB into an [array](arrays.md) of integers                                                                                             |
 | 
			
		||||
| `as_string`                     | _none_                                                                                                                                                                                                             | converts the BLOB into a [string](strings-chars.md) (the byte stream is interpreted as UTF-8)                                                        |
 | 
			
		||||
| `get`                           | position, counting from end if < 0                                                                                                                                                                                 | gets a copy of the byte at a certain position (0 if the position is not valid)                                                                       |
 | 
			
		||||
| `set`                           | <ol><li>position, counting from end if < 0</li><li>new byte value</li></ol>                                                                                                                                        | sets a certain position to a new value (no effect if the position is not valid)                                                                      |
 | 
			
		||||
| `push`, `append`, `+=` operator | <ol><li>BLOB</li><li>byte to append</li></ol>                                                                                                                                                                      | appends a byte to the end                                                                                                                            |
 | 
			
		||||
| `append`, `+=` operator         | <ol><li>BLOB</li><li>BLOB to append</li></ol>                                                                                                                                                                      | concatenates the second BLOB to the end of the first                                                                                                 |
 | 
			
		||||
| `append`, `+=` operator         | <ol><li>BLOB</li><li>[string/character](strings-chars.md) to append</li></ol>                                                                                                                                      | concatenates a [string/character](strings-chars.md) (as UTF-8 encoded byte-stream) to the end of the BLOB                                            |
 | 
			
		||||
| `+` operator                    | <ol><li>first BLOB</li><li>[string](strings-chars.md) to append</li></ol>                                                                                                                                          | creates a new [string](strings-chars.md) by concatenating the BLOB (as UTF-8 encoded byte-stream) with the the [string](strings-chars.md)            |
 | 
			
		||||
| `+` operator                    | <ol><li>[string](strings-chars.md)</li><li>BLOB to append</li></ol>                                                                                                                                                | creates a new [string](strings-chars.md) by concatenating the BLOB (as UTF-8 encoded byte-stream) to the end of the [string](strings-chars.md)       |
 | 
			
		||||
| `+` operator                    | <ol><li>first BLOB</li><li>second BLOB</li></ol>                                                                                                                                                                   | concatenates the first BLOB with the second                                                                                                          |
 | 
			
		||||
| `==` operator                   | <ol><li>first BLOB</li><li>second BLOB</li></ol>                                                                                                                                                                   | are two BLOB's the same?                                                                                                                             |
 | 
			
		||||
| `!=` operator                   | <ol><li>first BLOB</li><li>second BLOB</li></ol>                                                                                                                                                                   | are two BLOB's different?                                                                                                                            |
 | 
			
		||||
| `insert`                        | <ol><li>position, counting from end if < 0, end if ≥ length</li><li>byte to insert</li></ol>                                                                                                                       | inserts a byte at a certain position                                                                                                                 |
 | 
			
		||||
| `pop`                           | _none_                                                                                                                                                                                                             | removes the last byte and returns it (0 if empty)                                                                                                    |
 | 
			
		||||
| `shift`                         | _none_                                                                                                                                                                                                             | removes the first byte and returns it (0 if empty)                                                                                                   |
 | 
			
		||||
| `extract`                       | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>_(optional)_ number of bytes to extract, none if ≤ 0</li></ol>                                                                           | extracts a portion of the BLOB into a new BLOB                                                                                                       |
 | 
			
		||||
| `extract`                       | [range](ranges.md) of bytes to extract, from beginning if ≤ 0, to end if ≥ length                                                                                                                                  | extracts a portion of the BLOB into a new BLOB                                                                                                       |
 | 
			
		||||
| `remove`                        | position, counting from end if < 0                                                                                                                                                                                 | removes a byte at a particular position and returns it (0 if the position is not valid)                                                              |
 | 
			
		||||
| `reverse`                       | _none_                                                                                                                                                                                                             | reverses the BLOB byte by byte                                                                                                                       |
 | 
			
		||||
| `len` method and property       | _none_                                                                                                                                                                                                             | returns the number of bytes in the BLOB                                                                                                              |
 | 
			
		||||
| `is_empty` method and property  | _none_                                                                                                                                                                                                             | returns `true` if the BLOB is empty                                                                                                                  |
 | 
			
		||||
| `pad`                           | <ol><li>target length</li><li>byte value to pad</li></ol>                                                                                                                                                          | pads the BLOB with a byte value to at least a specified length                                                                                       |
 | 
			
		||||
| `clear`                         | _none_                                                                                                                                                                                                             | empties the BLOB                                                                                                                                     |
 | 
			
		||||
| `truncate`                      | target length                                                                                                                                                                                                      | cuts off the BLOB at exactly a specified length (discarding all subsequent bytes)                                                                    |
 | 
			
		||||
| `chop`                          | target length                                                                                                                                                                                                      | cuts off the head of the BLOB, leaving the tail at exactly a specified length                                                                        |
 | 
			
		||||
| `contains`, `in` operator       | byte value to find                                                                                                                                                                                                 | does the BLOB contain a particular byte value?                                                                                                       |
 | 
			
		||||
| `split`                         | <ol><li>BLOB</li><li>position to split at, counting from end if < 0, end if ≥ length</li></ol>                                                                                                                     | splits the BLOB into two BLOB's, starting from a specified position                                                                                  |
 | 
			
		||||
| `drain`                         | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to remove, none if ≤ 0</li></ol>                                                                                         | removes a portion of the BLOB, returning the removed bytes as a new BLOB                                                                             |
 | 
			
		||||
| `drain`                         | [range](ranges.md) of bytes to remove, from beginning if ≤ 0, to end if ≥ length                                                                                                                                   | removes a portion of the BLOB, returning the removed bytes as a new BLOB                                                                             |
 | 
			
		||||
| `retain`                        | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to retain, none if ≤ 0</li></ol>                                                                                         | retains a portion of the BLOB, removes all other bytes and returning them as a new BLOB                                                              |
 | 
			
		||||
| `retain`                        | [range](ranges.md) of bytes to retain, from beginning if ≤ 0, to end if ≥ length                                                                                                                                   | retains a portion of the BLOB, removes all other bytes and returning them as a new BLOB                                                              |
 | 
			
		||||
| `splice`                        | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to remove, none if ≤ 0</li><li>BLOB to insert</li></ol>                                                                  | replaces a portion of the BLOB with another (not necessarily of the same length as the replaced portion)                                             |
 | 
			
		||||
| `splice`                        | <ol><li>[range](ranges.md) of bytes to remove, from beginning if ≤ 0, to end if ≥ length</li><li>BLOB to insert                                                                                                    | replaces a portion of the BLOB with another (not necessarily of the same length as the replaced portion)                                             |
 | 
			
		||||
| `parse_le_int`                  | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to parse, 8 if > 8 (4 under 32-bit), none if ≤ 0</li></ol>                                                               | parses an integer at the particular offset in little-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)              |
 | 
			
		||||
| `parse_le_int`                  | [range](ranges.md) of bytes to parse, from beginning if ≤ 0, to end if ≥ length (up to 8 bytes, 4 under 32-bit)                                                                                                    | parses an integer at the particular offset in little-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)              |
 | 
			
		||||
| `parse_be_int`                  | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to parse, 8 if > 8 (4 under 32-bit), none if ≤ 0</li></ol>                                                               | parses an integer at the particular offset in big-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)                 |
 | 
			
		||||
| `parse_be_int`                  | [range](ranges.md) of bytes to parse, from beginning if ≤ 0, to end if ≥ length (up to 8 bytes, 4 under 32-bit)                                                                                                    | parses an integer at the particular offset in big-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)                 |
 | 
			
		||||
| `parse_le_float`                | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to parse, 8 if > 8 (4 under 32-bit), none if ≤ 0</li></ol>                                                               | parses a floating-point number at the particular offset in little-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored) |
 | 
			
		||||
| `parse_le_float`                | [range](ranges.md) of bytes to parse, from beginning if ≤ 0, to end if ≥ length (up to 8 bytes, 4 under 32-bit)                                                                                                    | parses a floating-point number at the particular offset in little-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored) |
 | 
			
		||||
| `parse_be_float`                | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to parse, 8 if > 8 (4 under 32-bit), none if ≤ 0</li></ol>                                                               | parses a floating-point number at the particular offset in big-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)    |
 | 
			
		||||
| `parse_be_float`                | [range](ranges.md) of bytes to parse, from beginning if ≤ 0, to end if ≥ length (up to 8 bytes, 4 under 32-bit)                                                                                                    | parses a floating-point number at the particular offset in big-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)    |
 | 
			
		||||
| `write_le`                      | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to write, 8 if > 8 (4 under 32-bit), none if ≤ 0</li><li>integer or floating-point value</li></ol>                       | writes a value at the particular offset in little-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)                 |
 | 
			
		||||
| `write_le`                      | <ol><li>[range](ranges.md) of bytes to write, from beginning if ≤ 0, to end if ≥ length (up to 8 bytes, 4 under 32-bit)</li><li>integer or floating-point value</li></ol>                                          | writes a value at the particular offset in little-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)                 |
 | 
			
		||||
| `write_be`                      | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to write, 8 if > 8 (4 under 32-bit), none if ≤ 0</li><li>integer or floating-point value</li></ol>                       | writes a value at the particular offset in big-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)                    |
 | 
			
		||||
| `write_be`                      | <ol><li>[range](ranges.md) of bytes to write, from beginning if ≤ 0, to end if ≥ length (up to 8 bytes, 4 under 32-bit)</li><li>integer or floating-point value</li></ol>                                          | writes a value at the particular offset in big-endian byte order (if not enough bytes, zeros are padded; extra bytes are ignored)                    |
 | 
			
		||||
| `write_utf8`                    | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of bytes to write, none if ≤ 0, to end if ≥ length</li><li>[string](strings-chars.md) to write</li></ol>                          | writes a [string](strings-chars.md) to the particular offset in UTF-8 encoding                                                                       |
 | 
			
		||||
| `write_utf8`                    | <ol><li>[range](ranges.md) of bytes to write, from beginning if ≤ 0, to end if ≥ length, to end if ≥ length</li><li>[string](strings-chars.md) to write</li></ol>                                                  | writes a [string](strings-chars.md) to the particular offset in UTF-8 encoding                                                                       |
 | 
			
		||||
| `write_ascii`                   | <ol><li>start position, counting from end if < 0, end if ≥ length</li><li>number of [characters](strings-chars.md) to write, none if ≤ 0, to end if ≥ length</li><li>[string](strings-chars.md) to write</li></ol> | writes a [string](strings-chars.md) to the particular offset in 7-bit ASCII encoding (non-ASCII [characters](strings-chars.md) are skipped)          |
 | 
			
		||||
| `write_ascii`                   | <ol><li>[range](ranges.md) of bytes to write, from beginning if ≤ 0, to end if ≥ length, to end if ≥ length</li><li>[string](strings-chars.md) to write</li></ol>                                                  | writes a [string](strings-chars.md) to the particular offset in 7-bit ASCII encoding (non-ASCII [characters](strings-chars.md) are skipped)          |
 | 
			
		||||
@@ -1,113 +0,0 @@
 | 
			
		||||
Comments
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
Comments are C-style, including `/*` ... `*/` pairs for block comments and `//` for comments to the
 | 
			
		||||
end of the line.
 | 
			
		||||
 | 
			
		||||
Block comments can be nested.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let /* intruder comment */ name = "Bob";
 | 
			
		||||
 | 
			
		||||
// This is a very important one-line comment
 | 
			
		||||
 | 
			
		||||
/* This comment spans
 | 
			
		||||
   multiple lines, so it
 | 
			
		||||
   only makes sense that
 | 
			
		||||
   it is even more important */
 | 
			
		||||
 | 
			
		||||
/* Fear not, Rhai satisfies all nesting needs with nested comments:
 | 
			
		||||
   /*/*/*/*/**/*/*/*/*/
 | 
			
		||||
*/
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Doc-Comments
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
Comments starting with `///` (three slashes) or `/**` (two asterisks) are _doc-comments_.
 | 
			
		||||
 | 
			
		||||
Doc-comments can only appear in front of [function](functions.md) definitions, not any other elements.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
/// This is a valid one-line doc-comment
 | 
			
		||||
fn foo() {}
 | 
			
		||||
 | 
			
		||||
/** This is a
 | 
			
		||||
 ** valid block
 | 
			
		||||
 ** doc-comment
 | 
			
		||||
 **/
 | 
			
		||||
fn bar(x) {
 | 
			
		||||
   /// Syntax error: this doc-comment is invalid
 | 
			
		||||
   x + 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Syntax error: this doc-comment is invalid */
 | 
			
		||||
let x = 42;
 | 
			
		||||
 | 
			
		||||
/// Syntax error: this doc-comment is also invalid
 | 
			
		||||
{
 | 
			
		||||
   let x = 42;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
~~~admonish tip "Tip: Special cases"
 | 
			
		||||
 | 
			
		||||
Long streams of `//////`... and `/*****`... do _NOT_ form doc-comments.
 | 
			
		||||
This is consistent with popular comment block styles for C-like languages.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
///////////////////////////////  <- this is not a doc-comment
 | 
			
		||||
// This is not a doc-comment //  <- this is not a doc-comment
 | 
			
		||||
///////////////////////////////  <- this is not a doc-comment
 | 
			
		||||
 | 
			
		||||
// However, watch out for comment lines starting with '///'
 | 
			
		||||
 | 
			
		||||
//////////////////////////////////////////  <- this is not a doc-comment
 | 
			
		||||
/// This, however, IS a doc-comment!!! ///  <- doc-comment!
 | 
			
		||||
//////////////////////////////////////////  <- this is not a doc-comment
 | 
			
		||||
 | 
			
		||||
/****************************************
 | 
			
		||||
 *                                      *
 | 
			
		||||
 * This is also not a doc-comment block *
 | 
			
		||||
 * so we don't have to put this in      *
 | 
			
		||||
 * front of a function.                 *
 | 
			
		||||
 *                                      *
 | 
			
		||||
 ****************************************/
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Module Documentation
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
Comment lines starting with `//!` make up the _module documentation_.
 | 
			
		||||
 | 
			
		||||
They are used to document the containing [module](modules/index.md) –
 | 
			
		||||
or for a Rhai script file, to document the file itself.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
//! Documentation for this script file.
 | 
			
		||||
//! This script is used to calculate something and display the result.
 | 
			
		||||
 | 
			
		||||
fn calculate(x) {
 | 
			
		||||
   ...
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn display(msg) {
 | 
			
		||||
   //! Module documentation can be placed anywhere within the file.
 | 
			
		||||
   ...
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//! All module documentation lines will be collected into a single block.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For the example above, the module documentation block is:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
//! Documentation for this script file.
 | 
			
		||||
//! This script is used to calculate something and display the result.
 | 
			
		||||
//! Module documentation can be placed anywhere within the file.
 | 
			
		||||
//! All module documentation lines will be collected into a single block.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
Constants
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
Constants can be defined using the `const` keyword and are immutable.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
const X;            // 'X' is a constant '()'
 | 
			
		||||
 | 
			
		||||
const X = 40 + 2;   // 'X' is a constant 42
 | 
			
		||||
 | 
			
		||||
print(X * 2);       // prints 84
 | 
			
		||||
 | 
			
		||||
X = 123;            // <- syntax error: constant modified
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Naming"
 | 
			
		||||
 | 
			
		||||
Constants follow the same naming rules as [variables](variables.md),
 | 
			
		||||
but as a convention are often named with all-capital letters.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Automatic Global Module
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
When a [constant](constants.md) is declared at global scope, it is added to a special
 | 
			
		||||
[module](modules/index.md) called `global`.
 | 
			
		||||
 | 
			
		||||
[Functions](functions.md) can access those [constants](constants.md) via the special `global`
 | 
			
		||||
[module](modules/index.md).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
const CONSTANT = 42;        // this constant is automatically added to 'global'
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    const INNER = 0;        // this constant is not at global level
 | 
			
		||||
}                           // <- it goes away here
 | 
			
		||||
 | 
			
		||||
fn foo(x) {
 | 
			
		||||
    x *= global::CONSTANT;  // ok! 'CONSTANT' exists in 'global'
 | 
			
		||||
 | 
			
		||||
    x * global::INNER       // <- error: constant 'INNER' not found in 'global'
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,95 +0,0 @@
 | 
			
		||||
Value Conversions
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Convert Between Integer and Floating-Point
 | 
			
		||||
------------------------------------------
 | 
			
		||||
 | 
			
		||||
| Function     |        From type        |    To type     |
 | 
			
		||||
| ------------ | :---------------------: | :------------: |
 | 
			
		||||
| `to_int`     | floating-point, decimal |    integer     |
 | 
			
		||||
| `to_float`   |    integer, decimal     | floating-point |
 | 
			
		||||
| `to_decimal` | integer, floating-point |    decimal     |
 | 
			
		||||
 | 
			
		||||
That's it; for other conversions, register custom conversion functions.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = 42;                     // 'x' is an integer
 | 
			
		||||
 | 
			
		||||
let y = x * 100.0;              // integer and floating-point can inter-operate
 | 
			
		||||
 | 
			
		||||
let y = x.to_float() * 100.0;   // convert integer to floating-point with 'to_float'
 | 
			
		||||
 | 
			
		||||
let z = y.to_int() + x;         // convert floating-point to integer with 'to_int'
 | 
			
		||||
 | 
			
		||||
let d = y.to_decimal();         // convert floating-point to Decimal with 'to_decimal'
 | 
			
		||||
 | 
			
		||||
let w = z.to_decimal() + x;     // Decimal and integer can inter-operate
 | 
			
		||||
 | 
			
		||||
let c = 'X';                    // character
 | 
			
		||||
 | 
			
		||||
print(`c is '${c}' and its code is ${c.to_int()}`); // prints "c is 'X' and its code is 88"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Parse String into Number
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
| Function                    |         From type          |          To type          |
 | 
			
		||||
| --------------------------- | :------------------------: | :-----------------------: |
 | 
			
		||||
| `parse_int`                 | [string](strings-chars.md) |          integer          |
 | 
			
		||||
| `parse_int` with radix 2-36 | [string](strings-chars.md) | integer (specified radix) |
 | 
			
		||||
| `parse_float`               | [string](strings-chars.md) |      floating-point       |
 | 
			
		||||
| `parse_decimal`             | [string](strings-chars.md) |          decimal          |
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = parse_float("123.4");   // parse as floating-point
 | 
			
		||||
x == 123.4;
 | 
			
		||||
type_of(x) == "f64";
 | 
			
		||||
 | 
			
		||||
let x = parse_decimal("123.4"); // parse as Decimal value
 | 
			
		||||
type_of(x) == "decimal";
 | 
			
		||||
 | 
			
		||||
let x = 1234.to_decimal() / 10; // alternate method to create a Decimal value
 | 
			
		||||
type_of(x) == "decimal";
 | 
			
		||||
 | 
			
		||||
let dec = parse_int("42");      // parse as integer
 | 
			
		||||
dec == 42;
 | 
			
		||||
type_of(dec) == "i64";
 | 
			
		||||
 | 
			
		||||
let dec = parse_int("42", 10);  // radix = 10 is the default
 | 
			
		||||
dec == 42;
 | 
			
		||||
type_of(dec) == "i64";
 | 
			
		||||
 | 
			
		||||
let bin = parse_int("110", 2);  // parse as binary (radix = 2)
 | 
			
		||||
bin == 0b110;
 | 
			
		||||
type_of(bin) == "i64";
 | 
			
		||||
 | 
			
		||||
let hex = parse_int("ab", 16);  // parse as hex (radix = 16)
 | 
			
		||||
hex == 0xab;
 | 
			
		||||
type_of(hex) == "i64";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Format Numbers
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
| Function    | From type |          To type           |             Format             |
 | 
			
		||||
| ----------- | :-------: | :------------------------: | :----------------------------: |
 | 
			
		||||
| `to_binary` |  integer  | [string](strings-chars.md) | binary (i.e. only `1` and `0`) |
 | 
			
		||||
| `to_octal`  |  integer  | [string](strings-chars.md) |    octal (i.e. `0` ... `7`)    |
 | 
			
		||||
| `to_hex`    |  integer  | [string](strings-chars.md) |     hex (i.e. `0` ... `f`)     |
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 0x1234abcd;
 | 
			
		||||
 | 
			
		||||
x == 305441741;
 | 
			
		||||
 | 
			
		||||
x.to_string() == "305441741";
 | 
			
		||||
 | 
			
		||||
x.to_binary() == "10010001101001010101111001101";
 | 
			
		||||
 | 
			
		||||
x.to_octal() == "2215125715";
 | 
			
		||||
 | 
			
		||||
x.to_hex() == "1234abcd";
 | 
			
		||||
```
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
Do Loop
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
`do` loops have two opposite variants: `do` ... `while` and `do` ... `until`.
 | 
			
		||||
 | 
			
		||||
`continue` can be used to skip to the next iteration, by-passing all following statements;
 | 
			
		||||
`break` can be used to break out of the loop unconditionally.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 10;
 | 
			
		||||
 | 
			
		||||
do {
 | 
			
		||||
    x -= 1;
 | 
			
		||||
    if x < 6 { continue; }  // skip to the next iteration
 | 
			
		||||
    print(x);
 | 
			
		||||
    if x == 5 { break; }    // break out of do loop
 | 
			
		||||
} while x > 0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
do {
 | 
			
		||||
    x -= 1;
 | 
			
		||||
    if x < 6 { continue; }  // skip to the next iteration
 | 
			
		||||
    print(x);
 | 
			
		||||
    if x == 5 { break; }    // break out of do loop
 | 
			
		||||
} until x == 0;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Do Expression
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
`do` statements can also be used as _expressions_.
 | 
			
		||||
 | 
			
		||||
The `break` statement takes an optional expression that provides the return value.
 | 
			
		||||
 | 
			
		||||
The default return value of a `do` expression is `()`.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = 0;
 | 
			
		||||
 | 
			
		||||
// 'do' can be used just like an expression
 | 
			
		||||
let result = do {
 | 
			
		||||
    if is_magic_number(x) {
 | 
			
		||||
        // if the 'do' loop breaks here, return a specific value
 | 
			
		||||
        break get_magic_result(x);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    x += 1;
 | 
			
		||||
 | 
			
		||||
    // ... if the 'do' loop exits here, the return value is ()
 | 
			
		||||
} until x >= 100;
 | 
			
		||||
 | 
			
		||||
if result == () {
 | 
			
		||||
    print("Magic number not found!");
 | 
			
		||||
} else {
 | 
			
		||||
    print(`Magic result = ${result}!`);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,242 +0,0 @@
 | 
			
		||||
Dynamic Value Tag
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
Each dynamic value can contain a _tag_ that is `i32` and can contain any arbitrary 32-bit signed data.
 | 
			
		||||
 | 
			
		||||
On 32-bit targets, however, the tag is only `i16` (16-bit signed).
 | 
			
		||||
 | 
			
		||||
The tag defaults to zero.
 | 
			
		||||
 | 
			
		||||
```admonish bug.small "Value out of bounds"
 | 
			
		||||
 | 
			
		||||
It is an error to set a tag to a value beyond the bounds of `i32` (`i16` on 32-bit targets).
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 42;
 | 
			
		||||
 | 
			
		||||
x.tag == 0;             // tag defaults to zero
 | 
			
		||||
 | 
			
		||||
x.tag = 123;            // set tag value
 | 
			
		||||
 | 
			
		||||
set_tag(x, 123);        // 'set_tag' function also works
 | 
			
		||||
 | 
			
		||||
x.tag == 123;           // get updated tag value
 | 
			
		||||
 | 
			
		||||
x.tag() == 123;         // method also works
 | 
			
		||||
 | 
			
		||||
tag(x) == 123;          // function call style also works
 | 
			
		||||
 | 
			
		||||
x.tag[3..5] = 2;        // tag can be used as a bit-field
 | 
			
		||||
 | 
			
		||||
x.tag[3..5] == 2;
 | 
			
		||||
 | 
			
		||||
let y = x;
 | 
			
		||||
 | 
			
		||||
y.tag == 123;           // the tag is copied across assignment
 | 
			
		||||
 | 
			
		||||
y.tag = 3000000000;     // runtime error: 3000000000 is too large for 'i32'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Practical Applications
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
Attaching arbitrary information together with a value has a lot of practical uses.
 | 
			
		||||
 | 
			
		||||
### Identify code path
 | 
			
		||||
 | 
			
		||||
For example, it is easy to attach an ID number to a value to indicate how or why that value is
 | 
			
		||||
originally set.
 | 
			
		||||
 | 
			
		||||
This is tremendously convenient for debugging purposes where it is necessary to figure out which
 | 
			
		||||
code path a particular value went through.
 | 
			
		||||
 | 
			
		||||
After the script is verified, all tag assignment statements can simply be removed.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const ROUTE1 = 1;
 | 
			
		||||
const ROUTE2 = 2;
 | 
			
		||||
const ROUTE3 = 3;
 | 
			
		||||
const ERROR_ROUTE = 9;
 | 
			
		||||
 | 
			
		||||
fn some_complex_calculation(x) {
 | 
			
		||||
    let result;
 | 
			
		||||
 | 
			
		||||
    if some_complex_condition(x) {
 | 
			
		||||
        result = 42;
 | 
			
		||||
        result.tag = ROUTE1;        // record route #1
 | 
			
		||||
    } else if some_other_very_complex_condition(x) == 1 {
 | 
			
		||||
        result = 123;
 | 
			
		||||
        result.tag = ROUTE2;        // record route #2
 | 
			
		||||
    } else if some_non_understandable_calculation(x) > 0 {
 | 
			
		||||
        result = 0;
 | 
			
		||||
        result.tag = ROUTE3;        // record route #3
 | 
			
		||||
    } else {
 | 
			
		||||
        result = -1;
 | 
			
		||||
        result.tag = ERROR_ROUTE;   // record error
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result  // this value now contains the tag
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let my_result = some_complex_calculation(key);
 | 
			
		||||
 | 
			
		||||
// The code path that 'my_result' went through is now in its tag.
 | 
			
		||||
 | 
			
		||||
// It is now easy to trace how 'my_result' gets its final value.
 | 
			
		||||
 | 
			
		||||
print(`Result = ${my_result} and reason = ${my_result.tag}`);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Identify data source
 | 
			
		||||
 | 
			
		||||
It is convenient to use the tag value to record the _source_ of a piece of data.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = [0, 1, 2, 3, 42, 99, 123];
 | 
			
		||||
 | 
			
		||||
// Store the index number of each value into its tag before
 | 
			
		||||
// filtering out all even numbers, leaving only odd numbers
 | 
			
		||||
let filtered = x.map(|v, i| { v.tag = i; v }).filter(|v| v.is_odd());
 | 
			
		||||
 | 
			
		||||
// The tag now contains the original index position
 | 
			
		||||
 | 
			
		||||
for (data, i) in filtered {
 | 
			
		||||
    print(`${i + 1}: Value ${data} from position #${data.tag + 1}`);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Identify code conditions
 | 
			
		||||
 | 
			
		||||
The tag value may also contain a _[bit-field](bit-fields.md)_ of up to 32 (16 under 32-bit targets)
 | 
			
		||||
individual bits, recording up to 32 (or 16 under 32-bit targets) logic conditions that contributed
 | 
			
		||||
to the value.
 | 
			
		||||
 | 
			
		||||
Again, after the script is verified, all tag assignment statements can simply be removed.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
 | 
			
		||||
fn some_complex_calculation(x) {
 | 
			
		||||
    let result = x;
 | 
			
		||||
 | 
			
		||||
    // Check first condition
 | 
			
		||||
    if some_complex_condition() {
 | 
			
		||||
        result += 1;
 | 
			
		||||
        result.tag[0] = true;   // Set first bit in bit-field
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check second condition
 | 
			
		||||
    if some_other_very_complex_condition(x) == 1 {
 | 
			
		||||
        result *= 10;
 | 
			
		||||
        result.tag[1] = true;   // Set second bit in bit-field
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check third condition
 | 
			
		||||
    if some_non_understandable_calculation(x) > 0 {
 | 
			
		||||
        result -= 42;
 | 
			
		||||
        result.tag[2] = true;   // Set third bit in bit-field
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check result
 | 
			
		||||
    if result > 100 {
 | 
			
		||||
        result = 0;
 | 
			
		||||
        result.tag[3] = true;   // Set forth bit in bit-field
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let my_result = some_complex_calculation(42);
 | 
			
		||||
 | 
			
		||||
// The tag of 'my_result' now contains a bit-field indicating
 | 
			
		||||
// the result of each condition.
 | 
			
		||||
 | 
			
		||||
// It is now easy to trace how 'my_result' gets its final value.
 | 
			
		||||
// Use indexing on the tag to get at individual bits.
 | 
			
		||||
 | 
			
		||||
print(`Result = ${my_result}`);
 | 
			
		||||
print(`First condition = ${my_result.tag[0]}`);
 | 
			
		||||
print(`Second condition = ${my_result.tag[1]}`);
 | 
			
		||||
print(`Third condition = ${my_result.tag[2]}`);
 | 
			
		||||
print(`Result check = ${my_result.tag[3]}`);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Return auxillary info
 | 
			
		||||
 | 
			
		||||
Sometimes it is useful to return auxillary info from a [function](functions.md).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Verify Bell's Inequality by calculating a norm
 | 
			
		||||
// and comparing it with a hypotenuse.
 | 
			
		||||
// https://en.wikipedia.org/wiki/Bell%27s_theorem
 | 
			
		||||
//
 | 
			
		||||
// Returns the smaller of the norm or hypotenuse.
 | 
			
		||||
// Tag is 1 if norm <= hypo, 0 if otherwise.
 | 
			
		||||
fn bells_inequality(x, y, z) {
 | 
			
		||||
    let norm = sqrt(x ** 2 + y ** 2);
 | 
			
		||||
    let result;
 | 
			
		||||
 | 
			
		||||
    if norm <= z {
 | 
			
		||||
        result = norm;
 | 
			
		||||
        result.tag = 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        result = z;
 | 
			
		||||
        result.tag = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let dist = bells_inequality(x, y, z);
 | 
			
		||||
 | 
			
		||||
print(`Value = ${dist}`);
 | 
			
		||||
 | 
			
		||||
if dist.tag == 1 {
 | 
			
		||||
    print("Local realism maintained! Einstein rules!");
 | 
			
		||||
} else {
 | 
			
		||||
    print("Spooky action at a distance detected! Einstein will hate this...");
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Poor-man's tuples
 | 
			
		||||
 | 
			
		||||
Rhai does not have _tuples_ (nor does JavaScript in this sense).
 | 
			
		||||
 | 
			
		||||
Similar to the JavaScript situation, practical alternatives using Rhai include returning an
 | 
			
		||||
[object map](object-maps.md) or an [array](arrays.md).
 | 
			
		||||
 | 
			
		||||
Both of these alternatives, however, incur overhead that may be wasteful when the amount of
 | 
			
		||||
additional information is small – e.g. in many cases, a single `bool`, or a small number.
 | 
			
		||||
 | 
			
		||||
To return a number of _small_ values from [functions](functions.md), the tag value as a
 | 
			
		||||
[bit-field](bit-fields.md) is an ideal container without resorting to a full-blown
 | 
			
		||||
[object map](object-maps.md) or [array](arrays.md).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// This function essentially returns a tuple of four numbers:
 | 
			
		||||
// (result, a, b, c)
 | 
			
		||||
fn complex_calc(x, y, z) {
 | 
			
		||||
    let a = x + y;
 | 
			
		||||
    let b = x - y + z;
 | 
			
		||||
    let c = (a + b) * z / y;
 | 
			
		||||
    let r = do_complex_calculation(a, b, c);
 | 
			
		||||
 | 
			
		||||
    // Store 'a', 'b' and 'c' into tag if they are small
 | 
			
		||||
    r.tag[0..8] = a;
 | 
			
		||||
    r.tag[8..16] = b;
 | 
			
		||||
    r.tag[16..32] = c;
 | 
			
		||||
 | 
			
		||||
    r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deconstruct the tuple
 | 
			
		||||
let result = complex_calc(x, y, z);
 | 
			
		||||
let a = r.tag[0..8];
 | 
			
		||||
let b = r.tag[8..16];
 | 
			
		||||
let c = r.tag[16..32];
 | 
			
		||||
```
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
Dynamic Values
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
A dynamic value can be _any_ type.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 42;         // value is an integer
 | 
			
		||||
 | 
			
		||||
x = 123.456;        // value is now a floating-point number
 | 
			
		||||
 | 
			
		||||
x = "hello";        // value is now a string
 | 
			
		||||
 | 
			
		||||
x = x.len > 0;      // value is now a boolean
 | 
			
		||||
 | 
			
		||||
x = [x];            // value is now an array
 | 
			
		||||
 | 
			
		||||
x = #{x: x};        // value is now an object map
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Use `type_of()` to Get Value Type
 | 
			
		||||
---------------------------------
 | 
			
		||||
 | 
			
		||||
Because [`type_of()`](type-of.md) a dynamic value returns the type of the actual value,
 | 
			
		||||
it is usually used to perform type-specific actions based on the actual value's type.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let mystery = get_some_dynamic_value();
 | 
			
		||||
 | 
			
		||||
switch type_of(mystery) {
 | 
			
		||||
    "()" => print("Hey, I got the unit () here!"),
 | 
			
		||||
    "i64" => print("Hey, I got an integer here!"),
 | 
			
		||||
    "f64" => print("Hey, I got a float here!"),
 | 
			
		||||
    "decimal" => print("Hey, I got a decimal here!"),
 | 
			
		||||
    "range" => print("Hey, I got an exclusive range here!"),
 | 
			
		||||
    "range=" => print("Hey, I got an inclusive range here!"),
 | 
			
		||||
    "string" => print("Hey, I got a string here!"),
 | 
			
		||||
    "bool" => print("Hey, I got a boolean here!"),
 | 
			
		||||
    "array" => print("Hey, I got an array here!"),
 | 
			
		||||
    "blob" => print("Hey, I got a BLOB here!"),
 | 
			
		||||
    "map" => print("Hey, I got an object map here!"),
 | 
			
		||||
    "Fn" => print("Hey, I got a function pointer here!"),
 | 
			
		||||
    "timestamp" => print("Hey, I got a time-stamp here!"),
 | 
			
		||||
    "TestStruct" => print("Hey, I got the TestStruct custom type here!"),
 | 
			
		||||
    _ => print(`I don't know what this is: ${type_of(mystery)}`)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
`eval` Function
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
Or "How to Shoot Yourself in the Foot even Easier"
 | 
			
		||||
--------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Saving the best for last, there is the ever-dreaded... `eval` [function](functions.md)!
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 10;
 | 
			
		||||
 | 
			
		||||
fn foo(x) { x += 12; x }
 | 
			
		||||
 | 
			
		||||
let script =
 | 
			
		||||
"
 | 
			
		||||
    let y = x;
 | 
			
		||||
    y += foo(y);
 | 
			
		||||
    x + y
 | 
			
		||||
";
 | 
			
		||||
 | 
			
		||||
let result = eval(script);      // <- look, JavaScript, we can also do this!
 | 
			
		||||
 | 
			
		||||
result == 42;
 | 
			
		||||
 | 
			
		||||
x == 10;                        // prints 10 - arguments are passed by value
 | 
			
		||||
y == 32;                        // prints 32 - variables defined in 'eval' persist!
 | 
			
		||||
 | 
			
		||||
eval("{ let z = y }");          // to keep a variable local, use a statements block
 | 
			
		||||
 | 
			
		||||
print(z);                       // <- error: variable 'z' not found
 | 
			
		||||
 | 
			
		||||
"print(42)".eval();             // <- nope... method-call style doesn't work with 'eval'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish danger.small "`eval` executes inside the current scope!"
 | 
			
		||||
 | 
			
		||||
Script segments passed to `eval` execute inside the _current_ scope, so they can access and modify
 | 
			
		||||
_everything_, including all [variables](variables.md) that are visible at that position in code!
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let script = "x += 32";
 | 
			
		||||
 | 
			
		||||
let x = 10;
 | 
			
		||||
eval(script);       // variable 'x' is visible!
 | 
			
		||||
print(x);           // prints 42
 | 
			
		||||
 | 
			
		||||
// The above is equivalent to:
 | 
			
		||||
let script = "x += 32";
 | 
			
		||||
let x = 10;
 | 
			
		||||
x += 32;
 | 
			
		||||
print(x);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`eval` can also be used to define new [variables](variables.md) and do other things normally forbidden inside
 | 
			
		||||
a [function](functions.md) call.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let script = "let x = 42";
 | 
			
		||||
eval(script);
 | 
			
		||||
print(x);           // prints 42
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Treat it as if the script segments are physically pasted in at the position of the `eval` call.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
~~~admonish warning.small "Cannot define new functions"
 | 
			
		||||
 | 
			
		||||
New [functions](functions.md) cannot be defined within an `eval` call, since [functions](functions.md)
 | 
			
		||||
can only be defined at the _global_ level!
 | 
			
		||||
~~~
 | 
			
		||||
@@ -1,203 +0,0 @@
 | 
			
		||||
Closures
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
Many functions in the standard API expect [function pointer](fn-ptr.md) as parameters.
 | 
			
		||||
 | 
			
		||||
For example:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Function 'double' defined here - used only once
 | 
			
		||||
fn double(x) { 2 * x }
 | 
			
		||||
 | 
			
		||||
// Function 'square' defined here - again used only once
 | 
			
		||||
fn square(x) { x * x }
 | 
			
		||||
 | 
			
		||||
let x = [1, 2, 3, 4, 5];
 | 
			
		||||
 | 
			
		||||
// Pass a function pointer to 'double'
 | 
			
		||||
let y = x.map(double);
 | 
			
		||||
 | 
			
		||||
// Pass a function pointer to 'square' using Fn(...) notation
 | 
			
		||||
let z = y.map(Fn("square"));
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Sometimes it gets tedious to define separate [functions](functions.md) only to dispatch them via
 | 
			
		||||
single [function pointers](fn-ptr.md) – essentially, those [functions](functions.md) are only
 | 
			
		||||
ever called in one place.
 | 
			
		||||
 | 
			
		||||
This scenario is especially common when simulating object-oriented programming ([OOP]).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Define functions one-by-one
 | 
			
		||||
fn obj_inc(x, y) { this.data += x * y; }
 | 
			
		||||
fn obj_dec(x) { this.data -= x; }
 | 
			
		||||
fn obj_print() { print(this.data); }
 | 
			
		||||
 | 
			
		||||
// Define object
 | 
			
		||||
let obj = #{
 | 
			
		||||
    data: 42,
 | 
			
		||||
    increment: obj_inc,     // use function pointers to
 | 
			
		||||
    decrement: obj_dec,     // refer to method functions
 | 
			
		||||
    print: obj_print
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Syntax
 | 
			
		||||
------
 | 
			
		||||
 | 
			
		||||
Closures have a syntax similar to Rust's _closures_ (they are _not_ the same).
 | 
			
		||||
 | 
			
		||||
> `|`_param 1_`,` _param 2_`,` ... `,` _param n_`|` _statement_  
 | 
			
		||||
>
 | 
			
		||||
> `|`_param 1_`,` _param 2_`,` ... `,` _param n_`| {` _statements_... `}`  
 | 
			
		||||
 | 
			
		||||
No parameters:
 | 
			
		||||
 | 
			
		||||
> `||` _statement_  
 | 
			
		||||
>
 | 
			
		||||
> `|| {` _statements_... `}`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Rewrite Using Closures
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
The above can be rewritten using closures.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = [1, 2, 3, 4, 5];
 | 
			
		||||
 | 
			
		||||
let y = x.map(|x| 2 * x);
 | 
			
		||||
 | 
			
		||||
let z = y.map(|x| x * x);
 | 
			
		||||
 | 
			
		||||
let obj = #{
 | 
			
		||||
    data: 42,
 | 
			
		||||
    increment: |x, y| this.data += x * y,   // one statement
 | 
			
		||||
    decrement: |x| this.data -= x,          // one statement
 | 
			
		||||
    print_obj: || {
 | 
			
		||||
        print(this.data);                   // full function body
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This de-sugars to:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Automatically generated...
 | 
			
		||||
fn anon_fn_0001(x) { 2 * x }
 | 
			
		||||
fn anon_fn_0002(x) { x * x }
 | 
			
		||||
fn anon_fn_0003(x, y) { this.data += x * y; }
 | 
			
		||||
fn anon_fn_0004(x) { this.data -= x; }
 | 
			
		||||
fn anon_fn_0005() { print(this.data); }
 | 
			
		||||
 | 
			
		||||
let x = [1, 2, 3, 4, 5];
 | 
			
		||||
 | 
			
		||||
let y = x.map(anon_fn_0001);
 | 
			
		||||
 | 
			
		||||
let z = y.map(anon_fn_0002);
 | 
			
		||||
 | 
			
		||||
let obj = #{
 | 
			
		||||
    data: 42,
 | 
			
		||||
    increment: anon_fn_0003,
 | 
			
		||||
    decrement: anon_fn_0004,
 | 
			
		||||
    print: anon_fn_0005
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Capture External Variables
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.side "Tip: `is_shared`"
 | 
			
		||||
 | 
			
		||||
Use `is_shared` to check whether a particular dynamic value is shared.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
Closures differ from standard functions because they can _captures_ [variables](variables.md) that
 | 
			
		||||
are not defined within the current scope, but are instead defined in an external scope – i.e.
 | 
			
		||||
where the it is created.
 | 
			
		||||
 | 
			
		||||
All [variables](variables.md) that are accessible during the time the closure is created are
 | 
			
		||||
automatically captured when they are used, as long as they are not shadowed by local
 | 
			
		||||
[variables](variables.md) defined within the function's.
 | 
			
		||||
 | 
			
		||||
The captured [variables](variables.md) are automatically converted into **reference-counted shared values**.
 | 
			
		||||
 | 
			
		||||
Therefore, similar to closures in many languages, these captured shared values persist through
 | 
			
		||||
reference counting, and may be read or modified even after the [variables](variables.md) that hold
 | 
			
		||||
them go out of scope and no longer exist.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 1;                          // a normal variable
 | 
			
		||||
 | 
			
		||||
x.is_shared() == false;
 | 
			
		||||
 | 
			
		||||
let f = |y| x + y;                  // variable 'x' is auto-curried (captured) into 'f'
 | 
			
		||||
 | 
			
		||||
x.is_shared() == true;              // 'x' is now a shared value!
 | 
			
		||||
 | 
			
		||||
f.call(2) == 3;                     // 1 + 2 == 3
 | 
			
		||||
 | 
			
		||||
x = 40;                             // changing 'x'...
 | 
			
		||||
 | 
			
		||||
f.call(2) == 42;                    // the value of 'x' is 40 because 'x' is shared
 | 
			
		||||
 | 
			
		||||
// The above de-sugars into something like this:
 | 
			
		||||
 | 
			
		||||
fn anon_0001(x, y) { x + y }        // parameter 'x' is inserted
 | 
			
		||||
 | 
			
		||||
make_shared(x);                     // convert variable 'x' into a shared value
 | 
			
		||||
 | 
			
		||||
let f = anon_0001.curry(x);         // shared 'x' is curried
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
~~~admonish bug "Beware: Captured variables are truly shared"
 | 
			
		||||
 | 
			
		||||
The example below is a typical tutorial sample for many languages to illustrate the traps
 | 
			
		||||
that may accompany capturing external [variables](variables.md) in closures.
 | 
			
		||||
 | 
			
		||||
It prints `9`, `9`, `9`, ... `9`, `9`, not `0`, `1`, `2`, ... `8`, `9`, because there is ever only
 | 
			
		||||
_one_ captured [variable](variables.md), and all ten closures capture the _same_
 | 
			
		||||
[variable](variables.md).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let list = [];
 | 
			
		||||
 | 
			
		||||
for i in 0..10 {
 | 
			
		||||
    list.push(|| print(i));     // the for loop variable 'i' is captured
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
list.len() == 10;               // 10 closures stored in the array
 | 
			
		||||
 | 
			
		||||
list[0].type_of() == "Fn";      // make sure these are closures
 | 
			
		||||
 | 
			
		||||
for f in list {
 | 
			
		||||
    f.call();                   // all references to 'i' point to the same variable!
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
~~~admonish danger "Prevent data races"
 | 
			
		||||
 | 
			
		||||
Data races are possible in Rhai scripts.
 | 
			
		||||
 | 
			
		||||
Avoid performing a method call on a captured shared [variable](variables.md) (which essentially
 | 
			
		||||
takes a mutable reference to the shared object) while using that same [variable](variables.md) as a
 | 
			
		||||
parameter in the method call – this is a sure-fire way to generate a data race error.
 | 
			
		||||
 | 
			
		||||
If a shared value is used as the `this` pointer in a method call to a closure function,
 | 
			
		||||
then the same shared value _must not_ be captured inside that function, or a data race
 | 
			
		||||
will occur and the script will terminate with an error.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 20;
 | 
			
		||||
 | 
			
		||||
x.is_shared() == false;         // 'x' not shared, so no data races
 | 
			
		||||
 | 
			
		||||
let f = |a| this += x + a;      // 'x' is captured in this closure
 | 
			
		||||
 | 
			
		||||
x.is_shared() == true;          // now 'x' is shared
 | 
			
		||||
 | 
			
		||||
x.call(f, 2);                   // <- error: data race detected on 'x'
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
Functions Metadata
 | 
			
		||||
==================
 | 
			
		||||
 | 
			
		||||
{{#title Functions Metadata}}
 | 
			
		||||
 | 
			
		||||
The _metadata_ of a [function](functions.md) means all relevant information related to a
 | 
			
		||||
[function's](functions.md) definition including:
 | 
			
		||||
 | 
			
		||||
1. Its callable name
 | 
			
		||||
 | 
			
		||||
2. Its access mode (public or [private](modules/export.md))
 | 
			
		||||
 | 
			
		||||
3. Its parameter names (if any)
 | 
			
		||||
 | 
			
		||||
4. Its purpose, in the form of [doc-comments](comments.md)
 | 
			
		||||
 | 
			
		||||
5. Usage notes, warnings, examples etc., in the form of [doc-comments](comments.md)
 | 
			
		||||
 | 
			
		||||
A [function's](functions.md) _signature_ encapsulates the first three pieces of information in a
 | 
			
		||||
single concise line of definition:
 | 
			
		||||
 | 
			
		||||
> `[private]` _name_ `(`_param 1_`,` _param 2_`,` ... `,` _param n_ `)`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Get Functions Metadata
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
The built-in [function](functions.md) `get_fn_metadata_list` returns an [array](arrays) of [object
 | 
			
		||||
maps](object-maps.md), each containing the metadata of one script-defined [function](functions.md)
 | 
			
		||||
in scope.
 | 
			
		||||
 | 
			
		||||
`get_fn_metadata_list` has a few versions taking different parameters:
 | 
			
		||||
 | 
			
		||||
| Signature                            | Description                                                                                                                                                      |
 | 
			
		||||
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `get_fn_metadata_list()`             | returns an [array](arrays.md) for _all_ script-defined [functions](functions.md)                                                                                 |
 | 
			
		||||
| `get_fn_metadata_list(name)`         | returns an [array](arrays.md) containing all script-defined [functions](functions.md) matching a specified name                                                  |
 | 
			
		||||
| `get_fn_metadata_list(name, params)` | returns an [array](arrays.md) containing all script-defined [functions](functions.md) matching a specified name and accepting the specified number of parameters |
 | 
			
		||||
 | 
			
		||||
The return value is an [array](arrays.md) of [object maps](object-maps.md) containing the following fields.
 | 
			
		||||
 | 
			
		||||
| Field          |                       Type                        | Optional? | Description                                                                                           |
 | 
			
		||||
| -------------- | :-----------------------------------------------: | :-------: | ----------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `namespace`    |            [string](strings-chars.md)             |  **yes**  | the module _namespace_ if the [function](functions.md) is defined within a [module](modules/index.md) |
 | 
			
		||||
| `access`       |            [string](strings-chars.md)             |    no     | `"public"` if the function is public,<br/>`"private"` if it is [private](modules/export.md)           |
 | 
			
		||||
| `name`         |            [string](strings-chars.md)             |    no     | [function](functions.md) name                                                                         |
 | 
			
		||||
| `params`       | [array](arrays.md) of [strings](strings-chars.md) |    no     | parameter names                                                                                       |
 | 
			
		||||
| `this_type`    |            [string](strings-chars.md)             |  **yes**  | restrict the type of `this` if the [function](functions.md) is a [method](fn_methods.md)              |
 | 
			
		||||
| `is_anonymous` |                      `bool`                       |    no     | is this [function](functions.md) an [anonymous function](fn-anon.md)?                                 |
 | 
			
		||||
| `comments`     | [array](arrays.md) of [strings](strings-chars.md) |  **yes**  | [doc-comments](comments.md), if any, one per line                                                     |
 | 
			
		||||
@@ -1,195 +0,0 @@
 | 
			
		||||
`this` – Simulating an Object Method
 | 
			
		||||
==========================================
 | 
			
		||||
 | 
			
		||||
```admonish warning.side "Functions are pure"
 | 
			
		||||
 | 
			
		||||
The only way for a script-defined [function](functions.md) to change an external value is via `this`.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Arguments passed to script-defined [functions](functions.md) are always by _value_ because
 | 
			
		||||
[functions](functions.md) are _pure_.
 | 
			
		||||
 | 
			
		||||
However, [functions](functions.md) can also be called in _method-call_ style:
 | 
			
		||||
 | 
			
		||||
> _object_ `.` _method_ `(` _parameters_ ... `)`
 | 
			
		||||
 | 
			
		||||
When a [function](functions.md) is called this way, the keyword `this` binds to the object in the
 | 
			
		||||
method call and can be changed.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn change() {       // note that the method does not need a parameter
 | 
			
		||||
    this = 42;      // 'this' binds to the object in method-call
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let x = 500;
 | 
			
		||||
 | 
			
		||||
x.change();         // call 'change' in method-call style, 'this' binds to 'x'
 | 
			
		||||
 | 
			
		||||
x == 42;            // 'x' is changed!
 | 
			
		||||
 | 
			
		||||
change();           // <- error: 'this' is unbound
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Elvis Operator
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
The [_Elvis_ operator](https://en.wikipedia.org/wiki/Elvis_operator) can be used to short-circuit
 | 
			
		||||
the method call when the object itself is `()`.
 | 
			
		||||
 | 
			
		||||
> _object_ `?.` _method_ `(` _parameters_ ... `)`
 | 
			
		||||
 | 
			
		||||
In the above, the _method_ is never called if _object_ is `()`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Restrict the Type of `this` in Function Definitions
 | 
			
		||||
---------------------------------------------------
 | 
			
		||||
 | 
			
		||||
```admonish tip.side.wide "Tip: Automatically global"
 | 
			
		||||
 | 
			
		||||
Methods defined this way are automatically exposed to the global namespace.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In many cases it may be desirable to implement _methods_ for different custom types using
 | 
			
		||||
script-defined [functions](functions.md).
 | 
			
		||||
 | 
			
		||||
### The Problem
 | 
			
		||||
 | 
			
		||||
Doing so is brittle and requires a lot of type checking code because there can only be one
 | 
			
		||||
[function](functions.md) definition for the same name and arity:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// Really painful way to define a method called 'do_update' on various data types
 | 
			
		||||
fn do_update(x) {
 | 
			
		||||
    switch type_of(this) {
 | 
			
		||||
        "i64" => this *= x,
 | 
			
		||||
        "string" => this.len += x,
 | 
			
		||||
        "bool" if this => this *= x,
 | 
			
		||||
        "bool" => this *= 42,
 | 
			
		||||
        "MyType" => this.update(x),
 | 
			
		||||
        "Strange-Type#Name::with_!@#symbols" => this.update(x),
 | 
			
		||||
        _ => throw `I don't know how to handle ${type_of(this)}`!`
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### The Solution
 | 
			
		||||
 | 
			
		||||
With a special syntax, it is possible to restrict a [function](functions.md) to be callable only
 | 
			
		||||
when the object pointed to by `this` is of a certain type:
 | 
			
		||||
 | 
			
		||||
> `fn`  _type name_ `.` _method_ `(` _parameters_ ... `)  {`  ...  `}`
 | 
			
		||||
 | 
			
		||||
or in quotes if the type name is not a valid identifier itself:
 | 
			
		||||
 | 
			
		||||
> `fn`  `"`_type name string_`"` `.` _method_ `(` _parameters_ ... `)  {`  ...  `}`
 | 
			
		||||
 | 
			
		||||
~~~admonish warning.small "Type name must be the same as `type_of`"
 | 
			
		||||
 | 
			
		||||
The _type name_ specified in front of the [function](functions.md) name must match the output of
 | 
			
		||||
[`type_of`](type-of.md) for the required type.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.small "Tip: `int` and `float`"
 | 
			
		||||
`int` can be used in place of the system integer type (usually `i64` or `i32`).
 | 
			
		||||
 | 
			
		||||
`float` can be used in place of the system floating-point type (usually `f64` or `f32`).
 | 
			
		||||
 | 
			
		||||
Using these make scripts more portable.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
### Examples
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
/// This 'do_update' can only be called on objects of type 'MyType' in method style
 | 
			
		||||
fn MyType.do_update(x, y) {
 | 
			
		||||
    this.update(x * y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// This 'do_update' can only be called on objects of type 'Strange-Type#Name::with_!@#symbols'
 | 
			
		||||
/// (which can be specified via 'Engine::register_type_with_name') in method style
 | 
			
		||||
fn "Strange-Type#Name::with_!@#symbols".do_update(x, y) {
 | 
			
		||||
    this.update(x * y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Define a blanket version
 | 
			
		||||
fn do_update(x, y) {
 | 
			
		||||
    this = `${this}, ${x}, ${y}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// This 'do_update' can only be called on integers in method style
 | 
			
		||||
fn int.do_update(x, y) {
 | 
			
		||||
    this += x * y
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let obj = create_my_type();     // 'x' is 'MyType'
 | 
			
		||||
 | 
			
		||||
obj.type_of() == "MyType";
 | 
			
		||||
 | 
			
		||||
obj.do_update(42, 123);         // ok!
 | 
			
		||||
 | 
			
		||||
let x = 42;                     // 'x' is an integer
 | 
			
		||||
 | 
			
		||||
x.type_of() == "i64";
 | 
			
		||||
 | 
			
		||||
x.do_update(42, 123);           // ok!
 | 
			
		||||
 | 
			
		||||
let x = true;                   // 'x' is a boolean
 | 
			
		||||
 | 
			
		||||
x.type_of() == "bool";
 | 
			
		||||
 | 
			
		||||
x.do_update(42, 123);           // <- this works because there is a blanket version
 | 
			
		||||
 | 
			
		||||
// Use 'is_def_fn' with three parameters to test for typed methods
 | 
			
		||||
is_def_fn("MyType", "do_update", 2) == true;
 | 
			
		||||
 | 
			
		||||
is_def_fn("int", "do_update", 2) == true;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Bind to `this` for Module Functions
 | 
			
		||||
-----------------------------------
 | 
			
		||||
 | 
			
		||||
### The Problem
 | 
			
		||||
 | 
			
		||||
The _method-call_ syntax is not possible for [functions](functions.md) [imported](modules/import.md)
 | 
			
		||||
from [modules](modules/index.md).
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import "my_module" as foo;
 | 
			
		||||
 | 
			
		||||
let x = 42;
 | 
			
		||||
 | 
			
		||||
x.foo::change_value(1);     // <- syntax error
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### The Solution
 | 
			
		||||
 | 
			
		||||
In order to call a [module](modules/index.md) [function](functions.md) as a method, it must be
 | 
			
		||||
defined with a restriction on the type of object pointed to by `this`:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
┌────────────────┐
 | 
			
		||||
│ my_module.rhai │
 | 
			
		||||
└────────────────┘
 | 
			
		||||
 | 
			
		||||
// This is a typed method function requiring 'this' to be an integer.
 | 
			
		||||
// Typed methods are automatically marked global when importing this module.
 | 
			
		||||
fn int.change_value(offset) {
 | 
			
		||||
    // 'this' is guaranteed to be an integer
 | 
			
		||||
    this += offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌───────────┐
 | 
			
		||||
│ main.rhai │
 | 
			
		||||
└───────────┘
 | 
			
		||||
 | 
			
		||||
import "my_module";
 | 
			
		||||
 | 
			
		||||
let x = 42;
 | 
			
		||||
 | 
			
		||||
x.change_value(1);          // ok!
 | 
			
		||||
 | 
			
		||||
x == 43;
 | 
			
		||||
```
 | 
			
		||||
@@ -1,247 +0,0 @@
 | 
			
		||||
Function Pointers
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
It is possible to store a _function pointer_ in a variable just like a normal value.
 | 
			
		||||
 | 
			
		||||
A function pointer is created via the `Fn` function, which takes a [string](strings-chars.md) parameter.
 | 
			
		||||
 | 
			
		||||
Call a function pointer via the `call` method.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Short-Hand Notation
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
```admonish warning.side "Not for native"
 | 
			
		||||
 | 
			
		||||
Native Rust functions cannot use this short-hand notation.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Having to write `Fn("foo")` in order to create a function pointer to the [function](functions.md)
 | 
			
		||||
`foo` is a chore, so there is a short-hand available.
 | 
			
		||||
 | 
			
		||||
A function pointer to any _script-defined_ [function](functions.md) _within the same script_ can be
 | 
			
		||||
obtained simply by referring to the [function's](functions.md) name.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn foo() { ... }        // function definition
 | 
			
		||||
 | 
			
		||||
let f = foo;            // function pointer to 'foo'
 | 
			
		||||
 | 
			
		||||
let f = Fn("foo");      // <- the above is equivalent to this
 | 
			
		||||
 | 
			
		||||
let g = bar;            // error: variable 'bar' not found
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The short-hand notation is particularly useful when passing [functions](functions.md) as
 | 
			
		||||
[closure](fn-closure.md) arguments.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn is_even(n) { n % 2 == 0 }
 | 
			
		||||
 | 
			
		||||
let array = [1, 2, 3, 4, 5];
 | 
			
		||||
 | 
			
		||||
array.filter(is_even);
 | 
			
		||||
 | 
			
		||||
array.filter(Fn("is_even"));    // <- the above is equivalent to this
 | 
			
		||||
 | 
			
		||||
array.filter(|n| n % 2 == 0);   // <- ... or this
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Built-in Functions
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
The following standard methods operate on function pointers.
 | 
			
		||||
 | 
			
		||||
| Function                           | Parameter(s) | Description                                                                                  |
 | 
			
		||||
| ---------------------------------- | ------------ | -------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `name` method and property         | _none_       | returns the name of the [function](functions.md) encapsulated by the function pointer        |
 | 
			
		||||
| `is_anonymous` method and property | _none_       | does the function pointer refer to an [anonymous function](fn-anon.md)?                      |
 | 
			
		||||
| `call`                             | _arguments_  | calls the [function](functions.md) matching the function pointer's name with the _arguments_ |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn foo(x) { 41 + x }
 | 
			
		||||
 | 
			
		||||
let func = Fn("foo");       // use the 'Fn' function to create a function pointer
 | 
			
		||||
 | 
			
		||||
let func = foo;             // <- short-hand: equivalent to 'Fn("foo")'
 | 
			
		||||
 | 
			
		||||
print(func);                // prints 'Fn(foo)'
 | 
			
		||||
 | 
			
		||||
let func = fn_name.Fn();    // <- error: 'Fn' cannot be called in method-call style
 | 
			
		||||
 | 
			
		||||
func.type_of() == "Fn";     // type_of() as function pointer is 'Fn'
 | 
			
		||||
 | 
			
		||||
func.name == "foo";
 | 
			
		||||
 | 
			
		||||
func.call(1) == 42;         // call a function pointer with the 'call' method
 | 
			
		||||
 | 
			
		||||
foo(1) == 42;               // <- the above de-sugars to this
 | 
			
		||||
 | 
			
		||||
call(func, 1);              // normal function call style also works for 'call'
 | 
			
		||||
 | 
			
		||||
let len = Fn("len");        // 'Fn' also works with registered native Rust functions
 | 
			
		||||
 | 
			
		||||
len.call("hello") == 5;
 | 
			
		||||
 | 
			
		||||
let fn_name = "hello";      // the function name does not have to exist yet
 | 
			
		||||
 | 
			
		||||
let hello = Fn(fn_name + "_world");
 | 
			
		||||
 | 
			
		||||
hello.call(0);              // error: function not found - 'hello_world (i64)'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish warning "Not First-Class Functions"
 | 
			
		||||
 | 
			
		||||
Beware that function pointers are _not_ first-class functions.
 | 
			
		||||
 | 
			
		||||
They are _syntactic sugar_ only, capturing only the _name_ of a [function](functions.md) to call.
 | 
			
		||||
They do not hold the actual [functions](functions.md).
 | 
			
		||||
 | 
			
		||||
The actual [function](functions.md) must be defined in the appropriate namespace for the call to
 | 
			
		||||
succeed.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish warning "Global Namespace Only"
 | 
			
		||||
 | 
			
		||||
Because of their dynamic nature, function pointers cannot refer to functions in
 | 
			
		||||
[`import`](modules/import.md)-ed [modules](modules/index.md).
 | 
			
		||||
 | 
			
		||||
They can only refer to [functions](functions.md) defined globally within the script
 | 
			
		||||
or a built-in function.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import "foo" as f;          // assume there is 'f::do_work()'
 | 
			
		||||
 | 
			
		||||
f::do_work();               // works!
 | 
			
		||||
 | 
			
		||||
let p = Fn("f::do_work");   // error: invalid function name
 | 
			
		||||
 | 
			
		||||
fn do_work_now() {          // call it from a local function
 | 
			
		||||
    f::do_work();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let p = Fn("do_work_now");
 | 
			
		||||
 | 
			
		||||
p.call();                   // works!
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Dynamic Dispatch
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
The purpose of function pointers is to enable rudimentary _dynamic dispatch_, meaning to determine,
 | 
			
		||||
at runtime, which function to call among a group.
 | 
			
		||||
 | 
			
		||||
Although it is possible to simulate dynamic dispatch via a number and a large
 | 
			
		||||
[`if-then-else-if`](if.md) statement, using function pointers significantly simplifies the code.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = some_calculation();
 | 
			
		||||
 | 
			
		||||
// These are the functions to call depending on the value of 'x'
 | 
			
		||||
fn method1(x) { ... }
 | 
			
		||||
fn method2(x) { ... }
 | 
			
		||||
fn method3(x) { ... }
 | 
			
		||||
 | 
			
		||||
// Traditional - using decision variable
 | 
			
		||||
let func = sign(x);
 | 
			
		||||
 | 
			
		||||
// Dispatch with if-statement
 | 
			
		||||
if func == -1 {
 | 
			
		||||
    method1(42);
 | 
			
		||||
} else if func == 0 {
 | 
			
		||||
    method2(42);
 | 
			
		||||
} else if func == 1 {
 | 
			
		||||
    method3(42);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Using pure function pointer
 | 
			
		||||
let func = if x < 0 {
 | 
			
		||||
    method1
 | 
			
		||||
} else if x == 0 {
 | 
			
		||||
    method2
 | 
			
		||||
} else if x > 0 {
 | 
			
		||||
    method3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Dynamic dispatch
 | 
			
		||||
func.call(42);
 | 
			
		||||
 | 
			
		||||
// Using functions map
 | 
			
		||||
let map = [ method1, method2, method3 ];
 | 
			
		||||
 | 
			
		||||
let func = sign(x) + 1;
 | 
			
		||||
 | 
			
		||||
// Dynamic dispatch
 | 
			
		||||
map[func].call(42);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Bind the `this` Pointer
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
When `call` is called as a _method_ but not on a function pointer, it is possible to dynamically dispatch
 | 
			
		||||
to a function call while binding the object in the method call to the `this` pointer of the function.
 | 
			
		||||
 | 
			
		||||
To achieve this, pass the function pointer as the _first_ argument to `call`:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn add(x) {                 // define function which uses 'this'
 | 
			
		||||
    this += x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let func = add;             // function pointer to 'add'
 | 
			
		||||
 | 
			
		||||
func.call(1);               // error: 'this' pointer is not bound
 | 
			
		||||
 | 
			
		||||
let x = 41;
 | 
			
		||||
 | 
			
		||||
func.call(x, 1);            // error: function 'add (i64, i64)' not found
 | 
			
		||||
 | 
			
		||||
call(func, x, 1);           // error: function 'add (i64, i64)' not found
 | 
			
		||||
 | 
			
		||||
x.call(func, 1);            // 'this' is bound to 'x', dispatched to 'func'
 | 
			
		||||
 | 
			
		||||
x == 42;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Beware that this only works for [_method-call_](fn-method.md) style.
 | 
			
		||||
Normal function-call style cannot bind the `this` pointer (for syntactic reasons).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Currying
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
It is possible to _curry_ a function pointer by providing partial (or all) arguments.
 | 
			
		||||
 | 
			
		||||
Currying is done via the `curry` keyword and produces a new function pointer which carries the
 | 
			
		||||
curried arguments.
 | 
			
		||||
 | 
			
		||||
When the curried function pointer is called, the curried arguments are inserted starting from the _left_.
 | 
			
		||||
 | 
			
		||||
The actual call arguments should be reduced by the number of curried arguments.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn mul(x, y) {                  // function with two parameters
 | 
			
		||||
    x * y
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let func = mul;                 // <- de-sugars to 'Fn("mul")'
 | 
			
		||||
 | 
			
		||||
func.call(21, 2) == 42;         // two arguments are required for 'mul'
 | 
			
		||||
 | 
			
		||||
let curried = func.curry(21);   // currying produces a new function pointer which
 | 
			
		||||
                                // carries 21 as the first argument
 | 
			
		||||
 | 
			
		||||
let curried = curry(func, 21);  // function-call style also works
 | 
			
		||||
 | 
			
		||||
curried.call(2) == 42;          // <- de-sugars to 'func.call(21, 2)'
 | 
			
		||||
                                //    only one argument is now required
 | 
			
		||||
```
 | 
			
		||||
@@ -1,253 +0,0 @@
 | 
			
		||||
For Loop
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
{{#include ../links.md}}
 | 
			
		||||
 | 
			
		||||
Iterating through a numeric [range](ranges.md) or an [array](arrays.md), or any iterable type,
 | 
			
		||||
is provided by the `for` ... `in` loop.
 | 
			
		||||
 | 
			
		||||
There are two alternative syntaxes, one including a counter variable:
 | 
			
		||||
 | 
			
		||||
> `for` _variable_ `in` _expression_ `{` ... `}`
 | 
			
		||||
>
 | 
			
		||||
> `for (` _variable_ `,` _counter_ `)` `in` _expression_ `{` ... `}`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Break or Continue
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
`continue` can be used to skip to the next iteration, by-passing all following statements;
 | 
			
		||||
`break` can be used to break out of the loop unconditionally.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
For Expression
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
`for` statements can also be used as _expressions_.
 | 
			
		||||
 | 
			
		||||
The `break` statement takes an optional expression that provides the return value.
 | 
			
		||||
 | 
			
		||||
The default return value of a `for` expression is `()`.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let a = [42, 123, 999, 0, true, "hello", "world!", 987.6543];
 | 
			
		||||
 | 
			
		||||
// 'for' can be used just like an expression
 | 
			
		||||
let index = for (item, count) in a {
 | 
			
		||||
    // if the 'for' loop breaks here, return a specific value
 | 
			
		||||
    switch item.type_of() {
 | 
			
		||||
        "i64" if item.is_even => break count,
 | 
			
		||||
        "f64" if item.to_int().is_even => break count,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ... if the 'for' loop exits here, the return value is ()
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if index == () {
 | 
			
		||||
    print("Magic number not found!");
 | 
			
		||||
} else {
 | 
			
		||||
    print(`Magic number found at index ${index}!`);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Counter Variable
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
The counter variable, if specified, starts from zero, incrementing upwards.
 | 
			
		||||
 | 
			
		||||
```js , no_run
 | 
			
		||||
let a = [42, 123, 999, 0, true, "hello", "world!", 987.6543];
 | 
			
		||||
 | 
			
		||||
// Loop through the array
 | 
			
		||||
for (item, count) in a {
 | 
			
		||||
    if x.type_of() == "string" {
 | 
			
		||||
        continue;                   // skip to the next iteration
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 'item' contains a copy of each element during each iteration
 | 
			
		||||
    // 'count' increments (starting from zero) for each iteration
 | 
			
		||||
    print(`Item #${count + 1} = ${item}`);
 | 
			
		||||
 | 
			
		||||
    if x == 42 { break; }           // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Iterate Through Arrays
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
Iterating through an [array](arrays.md) yields cloned _copies_ of each element.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let a = [1, 3, 5, 7, 9, 42];
 | 
			
		||||
 | 
			
		||||
// Loop through the array
 | 
			
		||||
for x in a {
 | 
			
		||||
    if x > 10 { continue; }         // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(x);
 | 
			
		||||
 | 
			
		||||
    if x == 42 { break; }           // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Iterate Through Strings
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
Iterating through a [string](strings-chars.md) yields individual [characters](strings-chars.md).
 | 
			
		||||
 | 
			
		||||
The `chars` method also allow iterating through characters in a [string](strings-chars.md),
 | 
			
		||||
optionally accepting the character position to start from (counting from the end if negative), as
 | 
			
		||||
well as the number of characters to iterate (defaults to all).
 | 
			
		||||
 | 
			
		||||
`char` also accepts a [range](ranges.md) which can be created via the `..` (exclusive) and `..=`
 | 
			
		||||
(inclusive) operators.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let s = "hello, world!";
 | 
			
		||||
 | 
			
		||||
// Iterate through all the characters.
 | 
			
		||||
for ch in s {
 | 
			
		||||
    print(ch);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Iterate starting from the 3rd character and stopping at the 7th.
 | 
			
		||||
for ch in s.chars(2, 5) {
 | 
			
		||||
    if ch > 'z' { continue; }       // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(ch);
 | 
			
		||||
 | 
			
		||||
    if x == '@' { break; }          // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Iterate starting from the 3rd character and stopping at the end.
 | 
			
		||||
for ch in s.chars(2..s.len) {
 | 
			
		||||
    if ch > 'z' { continue; }       // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(ch);
 | 
			
		||||
 | 
			
		||||
    if x == '@' { break; }          // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Iterate Through Numeric Ranges
 | 
			
		||||
------------------------------
 | 
			
		||||
 | 
			
		||||
[Ranges](ranges.md) are created via the `..` (exclusive) and `..=` (inclusive) operators.
 | 
			
		||||
 | 
			
		||||
The `range` function similarly creates exclusive [ranges](ranges.md), plus allowing optional step values.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Iterate starting from 0 and stopping at 49
 | 
			
		||||
// The step is assumed to be 1 when omitted for integers
 | 
			
		||||
for x in 0..50 {
 | 
			
		||||
    if x > 10 { continue; }         // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(x);
 | 
			
		||||
 | 
			
		||||
    if x == 42 { break; }           // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The 'range' function is just the same
 | 
			
		||||
for x in range(0, 50) {
 | 
			
		||||
    if x > 10 { continue; }         // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(x);
 | 
			
		||||
 | 
			
		||||
    if x == 42 { break; }           // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The 'range' function also takes a step
 | 
			
		||||
for x in range(0, 50, 3) {          // step by 3
 | 
			
		||||
    if x > 10 { continue; }         // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(x);
 | 
			
		||||
 | 
			
		||||
    if x == 42 { break; }           // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The 'range' function can also step backwards
 | 
			
		||||
for x in range(50..0, -3) {         // step down by -3
 | 
			
		||||
    if x < 10 { continue; }         // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(x);
 | 
			
		||||
 | 
			
		||||
    if x == 42 { break; }           // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// It works also for floating-point numbers
 | 
			
		||||
for x in range(5.0, 0.0, -2.0) {    // step down by -2.0
 | 
			
		||||
    if x < 10 { continue; }         // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(x);
 | 
			
		||||
 | 
			
		||||
    if x == 4.2 { break; }          // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Iterate Through Bit-Fields
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
The `bits` function allows iterating through an integer as a [bit-field](bit-fields.md).
 | 
			
		||||
 | 
			
		||||
`bits` optionally accepts the bit number to start from (counting from the most-significant-bit if
 | 
			
		||||
negative), as well as the number of bits to iterate (defaults all).
 | 
			
		||||
 | 
			
		||||
`bits` also accepts a [range](ranges.md) which can be created via the `..` (exclusive) and `..=`
 | 
			
		||||
(inclusive) operators.
 | 
			
		||||
 | 
			
		||||
```js , no_run
 | 
			
		||||
let x = 0b_1001110010_1101100010_1100010100;
 | 
			
		||||
let num_on = 0;
 | 
			
		||||
 | 
			
		||||
// Iterate through all the bits
 | 
			
		||||
for bit in x.bits() {
 | 
			
		||||
    if bit { num_on += 1; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print(`There are ${num_on} bits turned on!`);
 | 
			
		||||
 | 
			
		||||
const START = 3;
 | 
			
		||||
 | 
			
		||||
// Iterate through all the bits from 3 through 12
 | 
			
		||||
for (bit, index) in x.bits(START, 10) {
 | 
			
		||||
    print(`Bit #${index} is ${if bit { "ON" } else { "OFF" }}!`);
 | 
			
		||||
 | 
			
		||||
    if index >= 7 { break; }        // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Iterate through all the bits from 3 through 12
 | 
			
		||||
for (bit, index) in x.bits(3..=12) {
 | 
			
		||||
    print(`Bit #${index} is ${if bit { "ON" } else { "OFF" }}!`);
 | 
			
		||||
 | 
			
		||||
    if index >= 7 { break; }        // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Iterate Through Object Maps
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
Two methods, `keys` and `values`, return [arrays](arrays.md) containing cloned _copies_
 | 
			
		||||
of all property names and values of an [object map](object-maps.md), respectively.
 | 
			
		||||
 | 
			
		||||
These [arrays](arrays.md) can be iterated.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let map = #{a:1, b:3, c:5, d:7, e:9};
 | 
			
		||||
 | 
			
		||||
// Property names are returned in unsorted, random order
 | 
			
		||||
for x in map.keys() {
 | 
			
		||||
    if x > 10 { continue; }         // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(x);
 | 
			
		||||
 | 
			
		||||
    if x == 42 { break; }           // break out of for loop
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Property values are returned in unsorted, random order
 | 
			
		||||
for val in map.values() {
 | 
			
		||||
    print(val);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,210 +0,0 @@
 | 
			
		||||
Functions
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
Rhai supports defining functions in script via the `fn` keyword.
 | 
			
		||||
 | 
			
		||||
Valid function names are the same as valid [variable](variables.md) 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: `is_def_fn`"
 | 
			
		||||
 | 
			
		||||
Use `is_def_fn` 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
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
The last statement of a block is _always_ the block's return value regardless of whether it is
 | 
			
		||||
terminated with a semicolon `;`.
 | 
			
		||||
 | 
			
		||||
```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.
 | 
			
		||||
 | 
			
		||||
```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](variables.md) 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 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](modules/index.md) [imported](modules/import.md) 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](constants.md) is declared at global scope, it is added to a special
 | 
			
		||||
[module](modules/index.md) called [`global`](global.md).
 | 
			
		||||
 | 
			
		||||
Functions can access those [constants](constants.md) via the special [`global`](global.md)
 | 
			
		||||
[module](modules/index.md).
 | 
			
		||||
 | 
			
		||||
```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 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.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
Properties
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
Data types typically expose properties, which can be accessed in a Rust-like syntax:
 | 
			
		||||
 | 
			
		||||
> _object_ `.` _property_
 | 
			
		||||
>
 | 
			
		||||
> _object_ `.` _property_ `=` _value_ `;`
 | 
			
		||||
 | 
			
		||||
A runtime error is raised if the property does not exist for the object's data type.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Elvis Operator
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
The [_Elvis operator_](https://en.wikipedia.org/wiki/Elvis_operator) can be used to short-circuit
 | 
			
		||||
processing if the object itself is `()`.
 | 
			
		||||
 | 
			
		||||
> `// returns () if object is ()`  
 | 
			
		||||
> _object_ `?.` _property_
 | 
			
		||||
>
 | 
			
		||||
> `// no action if object is ()`  
 | 
			
		||||
> _object_ `?.` _property_ `=` _value_ `;`
 | 
			
		||||
@@ -1,85 +0,0 @@
 | 
			
		||||
If Statement
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
`if` statements follow C syntax.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
if foo(x) {
 | 
			
		||||
    print("It's true!");
 | 
			
		||||
} else if bar == baz {
 | 
			
		||||
    print("It's true again!");
 | 
			
		||||
} else if baz.is_foo() {
 | 
			
		||||
    print("Yet again true.");
 | 
			
		||||
} else if foo(bar - baz) {
 | 
			
		||||
    print("True again... this is getting boring.");
 | 
			
		||||
} else {
 | 
			
		||||
    print("It's finally false!");
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish warning.small "Braces are mandatory"
 | 
			
		||||
 | 
			
		||||
Unlike C, the condition expression does _not_ need to be enclosed in parentheses `(`...`)`, but all
 | 
			
		||||
branches of the `if` statement must be enclosed within braces `{`...`}`, even when there is only
 | 
			
		||||
one statement inside the branch.
 | 
			
		||||
 | 
			
		||||
There is no ambiguity regarding which `if` clause a branch belongs to.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Rhai is not C!
 | 
			
		||||
if (decision) print(42);
 | 
			
		||||
//            ^ syntax error, expecting '{'
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
If Expression
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
`if` statements can also be used as _expressions_, replacing the `? :` conditional operators in
 | 
			
		||||
other C-like languages.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// The following is equivalent to C: int x = 1 + (decision ? 42 : 123) / 2;
 | 
			
		||||
let x = 1 + if decision { 42 } else { 123 } / 2;
 | 
			
		||||
x == 22;
 | 
			
		||||
 | 
			
		||||
let x = if decision { 42 }; // no else branch defaults to '()'
 | 
			
		||||
x == ();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish danger.small "Statement before expression"
 | 
			
		||||
 | 
			
		||||
Beware that, like Rust, `if` is parsed primarily as a statement where it makes sense.
 | 
			
		||||
This is to avoid surprises.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn index_of(x) {
 | 
			
		||||
    // 'if' is parsed primarily as a statement
 | 
			
		||||
    if this.contains(x) {
 | 
			
		||||
        return this.find_index(x)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    -1
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The above will not be parsed as a single expression:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn index_of(x) {
 | 
			
		||||
    if this.contains(x) { return this.find_index(x) } - 1
 | 
			
		||||
    //                          error due to '() - 1' ^
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To force parsing as an expression, parentheses are required:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn calc_index(b, offset) {
 | 
			
		||||
    (if b { 1 } else { 0 }) + offset
 | 
			
		||||
//  ^---------------------^ parentheses
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
Rhai Language Reference
 | 
			
		||||
=======================
 | 
			
		||||
 | 
			
		||||
{{#title Rhai Language Reference}}
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
This is a stand-alone reference for the Rhai scripting language.
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
Indexing
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
```admonish tip.side "Tip: Non-integer index"
 | 
			
		||||
 | 
			
		||||
Some data types take an index that is not an integer.
 | 
			
		||||
For example, [object map](object-maps.md) indices are [strings](strings-chars.md).
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Some data types, such as [arrays](arrays.md), can be _indexed_ via a Rust-like syntax:
 | 
			
		||||
 | 
			
		||||
> _object_ `[` _index_ `]`
 | 
			
		||||
>
 | 
			
		||||
> _object_ `[` _index_ `]` `=` _value_ `;`
 | 
			
		||||
 | 
			
		||||
Usually, a runtime error is raised if the index value is out of bounds or does not exist for the
 | 
			
		||||
object's data type.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Elvis Notation
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
The [_Elvis notation_](https://en.wikipedia.org/wiki/Elvis_operator) is similar except that it
 | 
			
		||||
returns `()` if the object itself is `()`.
 | 
			
		||||
 | 
			
		||||
> `// returns () if object is ()`  
 | 
			
		||||
> _object_ `?[` _index_ `]`
 | 
			
		||||
>
 | 
			
		||||
> `// no action if object is ()`  
 | 
			
		||||
> _object_ `?[` _index_ `]` `=` _value_ `;`
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
Keywords
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
The following are reserved keywords in Rhai.
 | 
			
		||||
 | 
			
		||||
| Active keywords                                                                                                            | Reserved keywords                                          | Usage                                       |
 | 
			
		||||
| -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------- |
 | 
			
		||||
| `true`, `false`                                                                                                            |                                                            | constants                                   |
 | 
			
		||||
| [`let`](variable.md), [`const`](constant.md)                                                                               | `var`, `static`                                            | variables                                   |
 | 
			
		||||
| `is_shared`                                                                                                                |                                                            | _shared_ values                             |
 | 
			
		||||
|                                                                                                                            | `is`                                                       | type checking                               |
 | 
			
		||||
| [`if`](if.md), [`else`](if.md)                                                                                             | `goto`                                                     | control flow                                |
 | 
			
		||||
| [`switch`](switch.md)                                                                                                      | `match`, `case`                                            | switching and matching                      |
 | 
			
		||||
| [`do`](do.md), [`while`](while.md), [`loop`](loop.md), `until`, [`for`](for.md), [`in`](operators.md), `continue`, `break` |                                                            | looping                                     |
 | 
			
		||||
| [`fn`](functions.md), [`private`](modules/export.md), `is_def_fn`, `this`                                                  | `public`, `protected`, `new`                               | [functions](functions.md)                   |
 | 
			
		||||
| [`return`](return.md)                                                                                                      |                                                            | return values                               |
 | 
			
		||||
| [`throw`](throw.md), [`try`](try-catch.md), [`catch`](try-catch.md)                                                        |                                                            | [throw/catch](try-catch.md) exceptions      |
 | 
			
		||||
| [`import`](modules/import.md), [`export`](modules/export.md), `as`                                                         | `use`, `with`, `module`, `package`, `super`                | [modules](modules/index.md)                 |
 | 
			
		||||
| [`global`](global.md)                                                                                                      |                                                            | automatic global [module](modules/index.md) |
 | 
			
		||||
| [`Fn`](fn-ptr.md), `call`, [`curry`](fn-curry.md)                                                                          |                                                            | [function pointers](fn-ptr.md)              |
 | 
			
		||||
|                                                                                                                            | `spawn`, `thread`, `go`, `sync`, `async`, `await`, `yield` | threading/async                             |
 | 
			
		||||
| [`type_of`](type-of.md), [`print`](print-debug.md), [`debug`](print-debug.md), [`eval`](eval.md), `is_def_var`             |                                                            | special functions                           |
 | 
			
		||||
|                                                                                                                            | `default`, `void`, `null`, `nil`                           | special values                              |
 | 
			
		||||
 | 
			
		||||
```admonish warning.small
 | 
			
		||||
Keywords cannot become the name of a function or variable, even when they are disabled.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,59 +0,0 @@
 | 
			
		||||
Infinite Loop
 | 
			
		||||
=============
 | 
			
		||||
 | 
			
		||||
Infinite loops follow Rust syntax.
 | 
			
		||||
 | 
			
		||||
`continue` can be used to skip to the next iteration, by-passing all following statements;
 | 
			
		||||
`break` can be used to break out of the loop unconditionally.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 10;
 | 
			
		||||
 | 
			
		||||
loop {
 | 
			
		||||
    x -= 1;
 | 
			
		||||
 | 
			
		||||
    if x > 5 { continue; }  // skip to the next iteration
 | 
			
		||||
 | 
			
		||||
    print(x);
 | 
			
		||||
 | 
			
		||||
    if x == 0 { break; }    // break out of loop
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish danger.small "Remember the `break` statement"
 | 
			
		||||
 | 
			
		||||
A `loop` statement without a `break` statement inside its loop block is infinite.
 | 
			
		||||
There is no way for the loop to stop iterating.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Loop Expression
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
`loop` statements can also be used as _expressions_.
 | 
			
		||||
 | 
			
		||||
The `break` statement takes an optional expression that provides the return value.
 | 
			
		||||
 | 
			
		||||
The default return value of a `loop` expression is `()`.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = 0;
 | 
			
		||||
 | 
			
		||||
// 'loop' can be used just like an expression
 | 
			
		||||
let result = loop {
 | 
			
		||||
    if is_magic_number(x) {
 | 
			
		||||
        // if the loop breaks here, return a specific value
 | 
			
		||||
        break get_magic_result(x);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    x += 1;
 | 
			
		||||
 | 
			
		||||
    // ... if the loop exits here, the return value is ()
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if result == () {
 | 
			
		||||
    print("Magic number not found!");
 | 
			
		||||
} else {
 | 
			
		||||
    print(`Magic result = ${result}!`);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
Methods
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
Data types may have _methods_ that can be called:
 | 
			
		||||
 | 
			
		||||
> _object_ `.` _method_ `(` _parameters_ ... `)`
 | 
			
		||||
 | 
			
		||||
A runtime error is raised if the appropriate method does not exist for the object's data type.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Elvis Operator
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
The [_Elvis_ operator](https://en.wikipedia.org/wiki/Elvis_operator) can be used to short-circuit
 | 
			
		||||
the method call when the object itself is `()`.
 | 
			
		||||
 | 
			
		||||
> `// method is not called if object is ()`  
 | 
			
		||||
> _object_ `?.` _method_ `(` _parameters_ ... `)`
 | 
			
		||||
@@ -1,113 +0,0 @@
 | 
			
		||||
Export Variables, Functions and Sub-Modules From a Script
 | 
			
		||||
=========================================================
 | 
			
		||||
 | 
			
		||||
The easiest way to expose a collection of [functions](../functions.md) as a self-contained [module](index.md)
 | 
			
		||||
is to do it via a Rhai script itself.
 | 
			
		||||
 | 
			
		||||
The script text is evaluated.
 | 
			
		||||
 | 
			
		||||
[Variables](../variables.md) are then selectively exposed via the `export` statement.
 | 
			
		||||
 | 
			
		||||
[Functions](../functions.md) defined by the script are automatically exported, unless marked as `private`.
 | 
			
		||||
 | 
			
		||||
Modules loaded within this [module](index.md) at the global level become _sub-modules_ and are also
 | 
			
		||||
automatically exported.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Export Global Constants
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
The `export` statement, which can only be at global level, exposes a selected
 | 
			
		||||
[variable](../variables.md) as member of a [module](index.md).
 | 
			
		||||
 | 
			
		||||
[Variables](../variables.md) not exported are _private_ and hidden. They are merely used to
 | 
			
		||||
initialize the [module](index.md), but cannot be accessed from outside.
 | 
			
		||||
 | 
			
		||||
Everything exported from a [module](index.md) is **[constant](../constants.md)** (i.e. read-only).
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// This is a module script.
 | 
			
		||||
 | 
			
		||||
let hidden = 123;       // variable not exported - default hidden
 | 
			
		||||
let x = 42;             // this will be exported below
 | 
			
		||||
 | 
			
		||||
export x;               // the variable 'x' is exported under its own name
 | 
			
		||||
 | 
			
		||||
export const x = 42;    // convenient short-hand to declare a constant and export it
 | 
			
		||||
                        // under its own name
 | 
			
		||||
 | 
			
		||||
export let x = 123;     // variables can be exported as well, though it'll still be constant
 | 
			
		||||
 | 
			
		||||
export x as answer;     // the variable 'x' is exported under the alias 'answer'
 | 
			
		||||
                        // another script can load this module and access 'x' as 'module::answer'
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    let inner = 0;      // local variable - it disappears when the statements block ends,
 | 
			
		||||
                        //                  therefore it is not 'global' and cannot be exported
 | 
			
		||||
 | 
			
		||||
    export inner;       // <- syntax error: cannot export a local variable
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Multiple exports"
 | 
			
		||||
 | 
			
		||||
[Variables] can be exported under multiple names.
 | 
			
		||||
For example, the following exports three [variables]:
 | 
			
		||||
* `x` as `x` and `hello`
 | 
			
		||||
* `y` as `foo` and `bar`
 | 
			
		||||
* `z` as `z`
 | 
			
		||||
 | 
			
		||||
~~~js
 | 
			
		||||
export x;
 | 
			
		||||
export x as hello;
 | 
			
		||||
export y as foo;
 | 
			
		||||
export x as world;
 | 
			
		||||
export y as bar;
 | 
			
		||||
export z;
 | 
			
		||||
~~~
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Export Functions
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
```admonish info.side.wide "Private functions"
 | 
			
		||||
 | 
			
		||||
`private` [functions](../functions.md) are commonly called within the [module](index.md) only.
 | 
			
		||||
They cannot be accessed otherwise.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
All [functions](../functions.md) are automatically exported, _unless_ it is explicitly opt-out with
 | 
			
		||||
the `private` prefix.
 | 
			
		||||
 | 
			
		||||
[Functions](../functions.md) declared `private` are hidden to the outside.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// This is a module script.
 | 
			
		||||
 | 
			
		||||
fn inc(x) { x + 1 }     // script-defined function - default public
 | 
			
		||||
 | 
			
		||||
private fn foo() {}     // private function - hidden
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Sub-Modules
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
All loaded [modules](index.md) are automatically exported as sub-modules.
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.small "Tip: Skip exporting a module"
 | 
			
		||||
 | 
			
		||||
To prevent a [module](index.md) from being exported, load it inside a block statement
 | 
			
		||||
so that it goes away at the end of the block.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// This is a module script.
 | 
			
		||||
 | 
			
		||||
import "hello" as foo;      // <- exported
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    import "world" as bar;  // <- not exported
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
@@ -1,121 +0,0 @@
 | 
			
		||||
Import a Module
 | 
			
		||||
===============
 | 
			
		||||
 | 
			
		||||
`import` Statement
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
```admonish tip.side.wide "Tip"
 | 
			
		||||
 | 
			
		||||
A [module](index.md) that is only `import`-ed but not given any name is simply run.
 | 
			
		||||
 | 
			
		||||
This is a very simple way to run another script file from within a script.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A [module](index.md) can be _imported_ via the `import` statement, and be given a name.
 | 
			
		||||
 | 
			
		||||
Its members can be accessed via `::` similar to C++.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import "crypto_banner";         // run the script file 'crypto_banner.rhai' without creating an imported module
 | 
			
		||||
 | 
			
		||||
import "crypto" as lock;        // run the script file 'crypto.rhai' and import it as a module named 'lock'
 | 
			
		||||
 | 
			
		||||
const SECRET_NUMBER = 42;
 | 
			
		||||
 | 
			
		||||
let mod_file = `crypto_${SECRET_NUMBER}`;
 | 
			
		||||
 | 
			
		||||
import mod_file as my_mod;      // load the script file "crypto_42.rhai" and import it as a module named 'my_mod'
 | 
			
		||||
                                // notice that module path names can be dynamically constructed!
 | 
			
		||||
                                // any expression that evaluates to a string is acceptable after the 'import' keyword
 | 
			
		||||
 | 
			
		||||
lock::encrypt(secret);          // use functions defined under the module via '::'
 | 
			
		||||
 | 
			
		||||
lock::hash::sha256(key);        // sub-modules are also supported
 | 
			
		||||
 | 
			
		||||
print(lock::status);            // module variables are constants
 | 
			
		||||
 | 
			
		||||
lock::status = "off";           // <- runtime error: cannot modify a constant
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish info "Imports are _scoped_"
 | 
			
		||||
 | 
			
		||||
[Modules](index.md) imported via `import` statements are only accessible inside the relevant block scope.
 | 
			
		||||
 | 
			
		||||
~~~js
 | 
			
		||||
import "hacker" as h;           // import module - visible globally
 | 
			
		||||
 | 
			
		||||
if secured {                    // <- new block scope
 | 
			
		||||
    let mod = "crypt";
 | 
			
		||||
 | 
			
		||||
    import mod + "o" as c;      // import module (the path needs not be a constant string)
 | 
			
		||||
 | 
			
		||||
    let x = c::encrypt(key);    // use a function in the module
 | 
			
		||||
 | 
			
		||||
    h::hack(x);                 // global module 'h' is visible here
 | 
			
		||||
}                               // <- module 'c' disappears at the end of the block scope
 | 
			
		||||
 | 
			
		||||
h::hack(something);             // this works as 'h' is visible
 | 
			
		||||
 | 
			
		||||
c::encrypt(something);          // <- this causes a run-time error because
 | 
			
		||||
                                //    module 'c' is no longer available!
 | 
			
		||||
 | 
			
		||||
fn foo(something) {
 | 
			
		||||
    h::hack(something);         // <- this also works as 'h' is visible
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for x in 0..1000 {
 | 
			
		||||
    import "crypto" as c;       // <- importing a module inside a loop is a Very Bad Idea™
 | 
			
		||||
 | 
			
		||||
    c.encrypt(something);
 | 
			
		||||
}
 | 
			
		||||
~~~
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish note "Place `import` statements at the top"
 | 
			
		||||
 | 
			
		||||
`import` statements can appear anywhere a normal statement can be, but in the vast majority of cases they are
 | 
			
		||||
usually grouped at the top (beginning) of a script for manageability and visibility.
 | 
			
		||||
 | 
			
		||||
It is not advised to deviate from this common practice unless there is a _Very Good Reason™_.
 | 
			
		||||
 | 
			
		||||
Especially, do not place an `import` statement within a loop; doing so will repeatedly re-load the
 | 
			
		||||
same [module](index.md) during every iteration of the loop!
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
~~~admonish danger "Recursive imports"
 | 
			
		||||
 | 
			
		||||
Beware of _import cycles_ – i.e. recursively loading the same [module](index.md).
 | 
			
		||||
This is a sure-fire way to cause a stack overflow error.
 | 
			
		||||
 | 
			
		||||
For instance, importing itself always causes an infinite recursion:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
┌────────────┐
 | 
			
		||||
│ hello.rhai │
 | 
			
		||||
└────────────┘
 | 
			
		||||
 | 
			
		||||
import "hello" as foo;          // import itself - infinite recursion!
 | 
			
		||||
 | 
			
		||||
foo::do_something();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
[Modules](index.md) cross-referencing also cause infinite recursion:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
┌────────────┐
 | 
			
		||||
│ hello.rhai │
 | 
			
		||||
└────────────┘
 | 
			
		||||
 | 
			
		||||
import "world" as foo;
 | 
			
		||||
foo::do_something();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌────────────┐
 | 
			
		||||
│ world.rhai │
 | 
			
		||||
└────────────┘
 | 
			
		||||
 | 
			
		||||
import "hello" as bar;
 | 
			
		||||
bar::do_something_else();
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
Modules
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
Rhai allows organizing code into _modules_.
 | 
			
		||||
 | 
			
		||||
A module holds a collection of [functions](../functions.md), [constants](../constants.md) and sub-modules.
 | 
			
		||||
 | 
			
		||||
It may encapsulates a Rhai script together with the [functions](../functions.md) and
 | 
			
		||||
[constants](../constants.md) defined by that script.
 | 
			
		||||
 | 
			
		||||
Other scripts can then load this module and use the [functions](../functions.md) and
 | 
			
		||||
[constants](../constants.md) exported as if they were defined inside the same script.
 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
Numeric Functions
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
{{#title Numeric Functions}}
 | 
			
		||||
 | 
			
		||||
Integer Functions
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
The following standard functions operate on integers only.
 | 
			
		||||
 | 
			
		||||
| Function                      | Description                                                      |
 | 
			
		||||
| ----------------------------- | ---------------------------------------------------------------- |
 | 
			
		||||
| `is_odd` method and property  | returns `true` if the value is an odd number, otherwise `false`  |
 | 
			
		||||
| `is_even` method and property | returns `true` if the value is an even number, otherwise `false` |
 | 
			
		||||
| `min`                         | returns the smaller of two numbers, the first number if equal    |
 | 
			
		||||
| `max`                         | returns the larger of two numbers, the first number if equal     |
 | 
			
		||||
| `to_float`                    | convert the value into `f64` (`f32` under 32-bit)                |
 | 
			
		||||
| `to_decimal`                  | convert the value into decimal                                   |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Signed Numeric Functions
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
The following standard functions operate on signed numbers (including floating-point and decimal) only.
 | 
			
		||||
 | 
			
		||||
| Function                      | Description                                            |
 | 
			
		||||
| ----------------------------- | ------------------------------------------------------ |
 | 
			
		||||
| `abs`                         | absolute value                                         |
 | 
			
		||||
| `sign`                        | returns −1 if negative, +1 if positive, 0 if zero |
 | 
			
		||||
| `is_zero` method and property | returns `true` if the value is zero, otherwise `false` |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Floating-Point Functions
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
The following standard functions operate on floating-point and decimal numbers only.
 | 
			
		||||
 | 
			
		||||
| Category         | Decimal? | Functions                                                                                |
 | 
			
		||||
| ---------------- | :------: | ---------------------------------------------------------------------------------------- |
 | 
			
		||||
| Trigonometry     |   yes    | `sin`, `cos`, `tan`                                                                      |
 | 
			
		||||
| Trigonometry     |  **no**  | `sinh`, `cosh`, `tanh` in radians, `hypot(`_x_`,`_y_`)`                                  |
 | 
			
		||||
| Arc-trigonometry |  **no**  | `asin`, `acos`, `atan(`_v_`)`, `atan(`_x_`,`_y_`)`, `asinh`, `acosh`, `atanh` in radians |
 | 
			
		||||
| Square root      |   yes    | `sqrt`                                                                                   |
 | 
			
		||||
| Exponential      |   yes    | `exp` (base _e_)                                                                         |
 | 
			
		||||
| Logarithmic      |   yes    | `ln` (base _e_), `log` (base 10)                                                         |
 | 
			
		||||
| Logarithmic      |  **no**  | `log(`_x_`,`_base_`)`                                                                    |
 | 
			
		||||
| Rounding         |   yes    | `floor`, `ceiling`, `round`, `int`, `fraction` methods and properties                    |
 | 
			
		||||
| Conversion       |   yes    | [`to_int`](convert.md), [`to_decimal`](convert.md), [`to_float`](convert.md)             |
 | 
			
		||||
| Conversion       |  **no**  | `to_degrees`, `to_radians`                                                               |
 | 
			
		||||
| Comparison       |   yes    | `min`, `max` (also inter-operates with integers)                                         |
 | 
			
		||||
| Testing          |  **no**  | `is_nan`, `is_finite`, `is_infinite` methods and properties                              |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Decimal Rounding Functions
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
The following rounding methods operate on decimal numbers only.
 | 
			
		||||
 | 
			
		||||
| Rounding type     | Behavior                                    | Methods                                                      |
 | 
			
		||||
| ----------------- | ------------------------------------------- | ------------------------------------------------------------ |
 | 
			
		||||
| None              |                                             | `floor`, `ceiling`, `int`, `fraction` methods and properties |
 | 
			
		||||
| Banker's rounding | round to integer                            | `round` method and property                                  |
 | 
			
		||||
| Banker's rounding | round to specified number of decimal points | `round(`_decimal points_`)`                                  |
 | 
			
		||||
| Round up          | away from zero                              | `round_up(`_decimal points_`)`                               |
 | 
			
		||||
| Round down        | towards zero                                | `round_down(`_decimal points_`)`                             |
 | 
			
		||||
| Round half-up     | mid-point away from zero                    | `round_half_up(`_decimal points_`)`                          |
 | 
			
		||||
| Round half-down   | mid-point towards zero                      | `round_half_down(`_decimal points_`)`                        |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Parsing Functions
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
The following standard functions parse numbers.
 | 
			
		||||
 | 
			
		||||
| Function                      | Description                                                             |
 | 
			
		||||
| ----------------------------- | ----------------------------------------------------------------------- |
 | 
			
		||||
| [`parse_int`](convert.md)     | converts a [string](strings-chars.md) to integer with an optional radix |
 | 
			
		||||
| [`parse_float`](convert.md)   | converts a [string](strings-chars.md) to floating-point                 |
 | 
			
		||||
| [`parse_decimal`](convert.md) | converts a [string](strings-chars.md) to decimal                        |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Formatting Functions
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
The following standard functions convert integer numbers into a [string](strings-chars.md) of hex,
 | 
			
		||||
octal or binary representations.
 | 
			
		||||
 | 
			
		||||
| Function                  | Description                          |
 | 
			
		||||
| ------------------------- | ------------------------------------ |
 | 
			
		||||
| [`to_binary`](convert.md) | converts an integer number to binary |
 | 
			
		||||
| [`to_octal`](convert.md)  | converts an integer number to octal  |
 | 
			
		||||
| [`to_hex`](convert.md)    | converts an integer number to hex    |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Floating-point Constants
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
The following functions return standard mathematical constants.
 | 
			
		||||
 | 
			
		||||
| Function | Description               |
 | 
			
		||||
| -------- | ------------------------- |
 | 
			
		||||
| `PI`     | returns the value of π |
 | 
			
		||||
| `E`      | returns the value of _e_  |
 | 
			
		||||
@@ -1,136 +0,0 @@
 | 
			
		||||
Numeric Operators
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
{{#title Numeric Operators}}
 | 
			
		||||
 | 
			
		||||
Numeric operators generally follow C styles.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Unary Operators
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
| Operator | Description |
 | 
			
		||||
| :------: | ----------- |
 | 
			
		||||
|   `+`    | positive    |
 | 
			
		||||
|   `-`    | negative    |
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let number = +42;
 | 
			
		||||
 | 
			
		||||
number = -5;
 | 
			
		||||
 | 
			
		||||
number = -5 - +5;
 | 
			
		||||
 | 
			
		||||
-(-42) == +42;      // two '-' equals '+'
 | 
			
		||||
                    // beware: '++' and '--' are reserved symbols
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Binary Operators
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
|             Operator              | Description                                                      |    Result type     | Integer |     Floating-point     |      Decimal      |
 | 
			
		||||
| :-------------------------------: | ---------------------------------------------------------------- | :----------------: | :-----: | :--------------------: | :---------------: |
 | 
			
		||||
|             `+`, `+=`             | plus                                                             |      numeric       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|             `-`, `-=`             | minus                                                            |      numeric       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|             `*`, `*=`             | multiply                                                         |      numeric       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|             `/`, `/=`             | divide (integer division if acting on integer types)             |      numeric       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|             `%`, `%=`             | modulo (remainder)                                               |      numeric       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|            `**`, `**=`            | power/exponentiation                                             |      numeric       |   yes   | yes, also `FLOAT**INT` |      **no**       |
 | 
			
		||||
|            `<<`, `<<=`            | left bit-shift (if negative number of bits, shift right instead) |      numeric       |   yes   |         **no**         |      **no**       |
 | 
			
		||||
|            `>>`, `>>=`            | right bit-shift (if negative number of bits, shift left instead) |      numeric       |   yes   |         **no**         |      **no**       |
 | 
			
		||||
|             `&`, `&=`             | bit-wise _And_                                                   |      numeric       |   yes   |         **no**         |      **no**       |
 | 
			
		||||
| <code>\|</code>, <code>\|=</code> | bit-wise _Or_                                                    |      numeric       |   yes   |         **no**         |      **no**       |
 | 
			
		||||
|             `^`, `^=`             | bit-wise _Xor_                                                   |      numeric       |   yes   |         **no**         |      **no**       |
 | 
			
		||||
|               `==`                | equals to                                                        |       `bool`       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|               `!=`                | not equals to                                                    |       `bool`       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|                `>`                | greater than                                                     |       `bool`       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|               `>=`                | greater than or equals to                                        |       `bool`       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|                `<`                | less than                                                        |       `bool`       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|               `<=`                | less than or equals to                                           |       `bool`       |   yes   |   yes, also integer    | yes, also integer |
 | 
			
		||||
|               `..`                | exclusive range                                                  | [range](ranges.md) |   yes   |         **no**         |      **no**       |
 | 
			
		||||
|               `..=`               | inclusive range                                                  | [range](ranges.md) |   yes   |         **no**         |      **no**       |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = (1 + 2) * (6 - 4) / 2;  // arithmetic, with parentheses
 | 
			
		||||
 | 
			
		||||
let reminder = 42 % 10;         // modulo
 | 
			
		||||
 | 
			
		||||
let power = 42 ** 2;            // power
 | 
			
		||||
 | 
			
		||||
let left_shifted = 42 << 3;     // left shift
 | 
			
		||||
 | 
			
		||||
let right_shifted = 42 >> 3;    // right shift
 | 
			
		||||
 | 
			
		||||
let bit_op = 42 | 99;           // bit masking
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Floating-Point Interoperates with Integers
 | 
			
		||||
------------------------------------------
 | 
			
		||||
 | 
			
		||||
When one of the operands to a binary arithmetic operator is floating-point, it works with integer
 | 
			
		||||
for the other operand and the result is floating-point.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 41.0 + 1;               // float + integer
 | 
			
		||||
 | 
			
		||||
type_of(x) == "f64";            // result is float
 | 
			
		||||
 | 
			
		||||
let x = 21 * 2.0;               // float * integer
 | 
			
		||||
 | 
			
		||||
type_of(x) == "f64";
 | 
			
		||||
 | 
			
		||||
(x == 42) == true;              // float == integer
 | 
			
		||||
 | 
			
		||||
(10 < x) == true;               // integer < float
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Decimal Interoperates with Integers
 | 
			
		||||
-----------------------------------
 | 
			
		||||
 | 
			
		||||
When one of the operands to a binary arithmetic operator is decimal,
 | 
			
		||||
it works with integer for the other operand and the result is decimal.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let d = parse_decimal("2");
 | 
			
		||||
 | 
			
		||||
let x = d + 1;                  // decimal + integer
 | 
			
		||||
 | 
			
		||||
type_of(x) == "decimal";        // result is decimal
 | 
			
		||||
 | 
			
		||||
let x = 21 * d;                 // decimal * integer
 | 
			
		||||
 | 
			
		||||
type_of(x) == "decimal";
 | 
			
		||||
 | 
			
		||||
(x == 42) == true;              // decimal == integer
 | 
			
		||||
 | 
			
		||||
(10 < x) == true;               // integer < decimal
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Unary Before Binary
 | 
			
		||||
-------------------
 | 
			
		||||
 | 
			
		||||
In Rhai, unary operators take [precedence] over binary operators.  This is especially important to
 | 
			
		||||
remember when handling operators such as `**` which in some languages bind tighter than the unary
 | 
			
		||||
`-` operator.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
-2 + 2 == 0;
 | 
			
		||||
 | 
			
		||||
-2 - 2 == -4;
 | 
			
		||||
 | 
			
		||||
-2 * 2 == -4;
 | 
			
		||||
 | 
			
		||||
-2 / 2 == -1;
 | 
			
		||||
 | 
			
		||||
-2 % 2 == 0;
 | 
			
		||||
 | 
			
		||||
-2 ** 2 = 4;            // means: (-2) ** 2
 | 
			
		||||
                        // in some languages this means: -(2 ** 2)
 | 
			
		||||
```
 | 
			
		||||
@@ -1,56 +0,0 @@
 | 
			
		||||
Numbers
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Integers
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
Integer numbers follow C-style format with support for decimal, binary (`0b`), octal (`0o`) and hex (`0x`) notations.
 | 
			
		||||
 | 
			
		||||
Integers can also be conveniently manipulated as [bit-fields](bit-fields.md).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Floating-Point Numbers
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
Both decimal and scientific notations can be used to represent floating-point numbers.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Decimal Numbers
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
When rounding errors cannot be accepted, such as in financial calculations, use the decimal type,
 | 
			
		||||
which is a fixed-precision floating-point number with no rounding errors.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Number Literals
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
`_` separators can be added freely and are ignored within a number – except at the very
 | 
			
		||||
beginning or right after a decimal point (`.`).
 | 
			
		||||
 | 
			
		||||
| Sample             | Format                    |
 | 
			
		||||
| ------------------ | ------------------------- |
 | 
			
		||||
| `_123`             | _improper separator_      |
 | 
			
		||||
| `123_345`, `-42`   | decimal                   |
 | 
			
		||||
| `0o07_76`          | octal                     |
 | 
			
		||||
| `0xab_cd_ef`       | hex                       |
 | 
			
		||||
| `0b0101_1001`      | binary                    |
 | 
			
		||||
| `123._456`         | _improper separator_      |
 | 
			
		||||
| `123_456.78_9`     | normal floating-point     |
 | 
			
		||||
| `-42.`             | ending with decimal point |
 | 
			
		||||
| `123_456_.789e-10` | scientific notation       |
 | 
			
		||||
| `.456`             | _missing leading `0`_     |
 | 
			
		||||
| `123.456e_10`      | _improper separator_      |
 | 
			
		||||
| `123.e-10`         | _missing decimal `0`_     |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Floating-Point vs. Decimal
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
Decimal numbers represents a fixed-precision floating-point number which is popular with financial
 | 
			
		||||
calculations and other usage scenarios where round-off errors are not acceptable.
 | 
			
		||||
 | 
			
		||||
Decimal numbers take up more space (16 bytes each) than a standard floating-point number (4-8 bytes)
 | 
			
		||||
and is much slower in calculations due to the lack of CPU hardware support. Use it only when
 | 
			
		||||
necessary.
 | 
			
		||||
@@ -1,285 +0,0 @@
 | 
			
		||||
Object Maps
 | 
			
		||||
===========
 | 
			
		||||
 | 
			
		||||
Object maps are hash dictionaries. Properties are all dynamic values and can be freely added and retrieved.
 | 
			
		||||
 | 
			
		||||
[`type_of()`](type-of.md) an object map returns `"map"`.
 | 
			
		||||
 | 
			
		||||
~~~admonish tip "Tip: Object maps are _FAST_"
 | 
			
		||||
 | 
			
		||||
Normally, when [properties](getters-setters.md) are accessed, copies of the data values are made.
 | 
			
		||||
This is normally slow.
 | 
			
		||||
 | 
			
		||||
Object maps have special treatment – properties are accessed via _references_, meaning that
 | 
			
		||||
no copies of data values are made.
 | 
			
		||||
 | 
			
		||||
This makes object map access fast, especially when deep within a properties chain.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// 'obj' is a normal custom type
 | 
			
		||||
let x = obj.a.b.c.d;
 | 
			
		||||
 | 
			
		||||
// The above is equivalent to:
 | 
			
		||||
let a_value = obj.a;        // temp copy of 'a'
 | 
			
		||||
let b_value = a_value.b;    // temp copy of 'b'
 | 
			
		||||
let c_value = b_value.c;    // temp copy of 'c'
 | 
			
		||||
let d_value = c_value.d;    // temp copy of 'd'
 | 
			
		||||
let x = d_value;
 | 
			
		||||
 | 
			
		||||
// 'map' is an object map
 | 
			
		||||
let x = map.a.b.c.d;        // direct access to 'd'
 | 
			
		||||
                            // 'a', 'b' and 'c' are not copied
 | 
			
		||||
 | 
			
		||||
map.a.b.c.d = 42;           // directly modifies 'd' in 'a', 'b' and 'c'
 | 
			
		||||
                            // no copy of any property value is made
 | 
			
		||||
 | 
			
		||||
map.a.b.c.d.calc();         // directly calls 'calc' on 'd'
 | 
			
		||||
                            // no copy of any property value is made
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Literal Syntax
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
Object map literals are built within braces `#{` ... `}` with _name_`:`_value_ pairs separated by
 | 
			
		||||
commas `,`:
 | 
			
		||||
 | 
			
		||||
> `#{` _property_ `:` _value_`,` ... `,` _property_ `:` _value_ `}`
 | 
			
		||||
>
 | 
			
		||||
> `#{` _property_ `:` _value_`,` ... `,` _property_ `:` _value_ `,` `}`     `// trailing comma is OK`
 | 
			
		||||
 | 
			
		||||
The property _name_ can be a simple identifier following the same naming rules as
 | 
			
		||||
[variables](variables.md), or a [string literal](../appendix/literals.md) without interpolation.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Property Access Syntax
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
### Dot notation
 | 
			
		||||
 | 
			
		||||
The _dot notation_ allows only property names that follow the same naming rules as
 | 
			
		||||
[variables](variables.md).
 | 
			
		||||
 | 
			
		||||
> _object_ `.` _property_
 | 
			
		||||
 | 
			
		||||
### Elvis notation
 | 
			
		||||
 | 
			
		||||
The [_Elvis notation_](https://en.wikipedia.org/wiki/Elvis_operator) is similar to the _dot
 | 
			
		||||
notation_ except that it returns `()` if the object itself is `()`.
 | 
			
		||||
 | 
			
		||||
> `// returns () if object is ()`  
 | 
			
		||||
> _object_ `?.` _property_
 | 
			
		||||
>
 | 
			
		||||
> `// no action if object is ()`  
 | 
			
		||||
> _object_ `?.` _property_ `=` _value_ `;`
 | 
			
		||||
 | 
			
		||||
### Index notation
 | 
			
		||||
 | 
			
		||||
The _index notation_ allows setting/getting properties of arbitrary names (even the empty
 | 
			
		||||
[string](strings-chars.md)).
 | 
			
		||||
 | 
			
		||||
> _object_ `[` _property_ `]`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Handle Non-Existent Properties
 | 
			
		||||
------------------------------
 | 
			
		||||
 | 
			
		||||
Trying to read a non-existent property returns `()` instead of causing an error.
 | 
			
		||||
 | 
			
		||||
This is similar to JavaScript where accessing a non-existent property returns `undefined`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let map = #{ foo: 42 };
 | 
			
		||||
 | 
			
		||||
// Regular property access
 | 
			
		||||
let x = map.foo;            // x == 42
 | 
			
		||||
 | 
			
		||||
// Non-existent property
 | 
			
		||||
let x = map.bar;            // x == ()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Check for property existence
 | 
			
		||||
 | 
			
		||||
Use the [`in`](operators.md#in-operator) operator to check whether a property exists in an object-map.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let map = #{ foo: 42 };
 | 
			
		||||
 | 
			
		||||
"foo" in map == true;
 | 
			
		||||
 | 
			
		||||
"bar" in map == false;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Short-circuit non-existent property access
 | 
			
		||||
 | 
			
		||||
Use the [_Elvis operator_](https://en.wikipedia.org/wiki/Elvis_operator) (`?.`) to short-circuit
 | 
			
		||||
further processing if the object is `()`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
x.a.b.foo();        // <- error if 'x', 'x.a' or 'x.a.b' is ()
 | 
			
		||||
 | 
			
		||||
x.a.b = 42;         // <- error if 'x' or 'x.a' is ()
 | 
			
		||||
 | 
			
		||||
x?.a?.b?.foo();     // <- ok! returns () if 'x', 'x.a' or 'x.a.b' is ()
 | 
			
		||||
 | 
			
		||||
x?.a?.b = 42;       // <- ok even if 'x' or 'x.a' is ()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Default property value
 | 
			
		||||
 | 
			
		||||
Using the [null-coalescing operator](operators.md#null-coalescing-operator) to give non-existent
 | 
			
		||||
properties default values.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let map = #{ foo: 42 };
 | 
			
		||||
 | 
			
		||||
// Regular property access
 | 
			
		||||
let x = map.foo;            // x == 42
 | 
			
		||||
 | 
			
		||||
// Non-existent property
 | 
			
		||||
let x = map.bar;            // x == ()
 | 
			
		||||
 | 
			
		||||
// Default value for property
 | 
			
		||||
let x = map.bar ?? 42;      // x == 42
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Built-in Functions
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
The following methods operate on object maps.
 | 
			
		||||
 | 
			
		||||
| Function                  | Parameter(s)                                                                    | Description                                                                                                                                                                                                                                                   |
 | 
			
		||||
| ------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `get`                     | property name                                                                   | gets a copy of the value of a certain property (`()` if the property does not exist)                                                                                                                                                                          |
 | 
			
		||||
| `set`                     | <ol><li>property name</li><li>new element</li></ol>                             | sets a certain property to a new value (property is added if not already exists)                                                                                                                                                                              |
 | 
			
		||||
| `len`                     | _none_                                                                          | returns the number of properties                                                                                                                                                                                                                              |
 | 
			
		||||
| `is_empty`                | _none_                                                                          | returns `true` if the object map is empty                                                                                                                                                                                                                     |
 | 
			
		||||
| `clear`                   | _none_                                                                          | empties the object map                                                                                                                                                                                                                                        |
 | 
			
		||||
| `remove`                  | property name                                                                   | removes a certain property and returns it (`()` if the property does not exist)                                                                                                                                                                               |
 | 
			
		||||
| `+=` operator, `mixin`    | second object map                                                               | mixes in all the properties of the second object map to the first (values of properties with the same names replace the existing values)                                                                                                                      |
 | 
			
		||||
| `+` operator              | <ol><li>first object map</li><li>second object map</li></ol>                    | merges the first object map with the second                                                                                                                                                                                                                   |
 | 
			
		||||
| `==` operator             | <ol><li>first object map</li><li>second object map</li></ol>                    | are the two object maps the same (elements compared with the `==` operator, if defined)?                                                                                                                                                                      |
 | 
			
		||||
| `!=` operator             | <ol><li>first object map</li><li>second object map</li></ol>                    | are the two object maps different (elements compared with the `==` operator, if defined)?                                                                                                                                                                     |
 | 
			
		||||
| `fill_with`               | second object map                                                               | adds in all properties of the second object map that do not exist in the object map                                                                                                                                                                           |
 | 
			
		||||
| `contains`, `in` operator | property name                                                                   | does the object map contain a property of a particular name?                                                                                                                                                                                                  |
 | 
			
		||||
| `drain`                   | [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md)) | removes all elements (returning them) that return `true` when called with the predicate function taking the following parameters:<ol><li>key</li><li>_(optional)_ object map element (if omitted, the object map element is bound to `this`)</li></ol>        |
 | 
			
		||||
| `retain`                  | [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md)) | removes all elements (returning them) that do not return `true` when called with the predicate function taking the following parameters:<ol><li>key</li><li>_(optional)_ object map element (if omitted, the object map element is bound to `this`)</li></ol> |
 | 
			
		||||
| `filter`                  | [function pointer](fn-ptr.md) to predicate (usually a [closure](fn-closure.md)) | constructs a object map with all elements that return `true` when called with the predicate function taking the following parameters:<ol><li>key</li><li>_(optional)_ object map element (if omitted, the object map element is bound to `this`)</li></ol>    |
 | 
			
		||||
| `keys`                    | _none_                                                                          | returns an [array](arrays.md) of all the property names (in random order)                                                                                                                                                                                     |
 | 
			
		||||
| `values`                  | _none_                                                                          | returns an [array](arrays.md) of all the property values (in random order)                                                                                                                                                                                    |
 | 
			
		||||
| `to_json`                 | _none_                                                                          | returns a JSON representation of the object map (`()` is mapped to `null`, all other data types must be supported by JSON)                                                                                                                                    |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let y = #{              // object map literal with 3 properties
 | 
			
		||||
    a: 1,
 | 
			
		||||
    bar: "hello",
 | 
			
		||||
    "baz!$@": 123.456,  // like JavaScript, you can use any string as property names...
 | 
			
		||||
    "": false,          // even the empty string!
 | 
			
		||||
 | 
			
		||||
    `hello`: 999,       // literal strings are also OK
 | 
			
		||||
 | 
			
		||||
    a: 42,              // <- syntax error: duplicated property name
 | 
			
		||||
 | 
			
		||||
    `a${2}`: 42,        // <- syntax error: property name cannot have string interpolation
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
y.a = 42;               // access via dot notation
 | 
			
		||||
y.a == 42;
 | 
			
		||||
 | 
			
		||||
y.baz!$@ = 42;          // <- syntax error: only proper variable names allowed in dot notation
 | 
			
		||||
y."baz!$@" = 42;        // <- syntax error: strings not allowed in dot notation
 | 
			
		||||
y["baz!$@"] = 42;       // access via index notation is OK
 | 
			
		||||
 | 
			
		||||
"baz!$@" in y == true;  // use 'in' to test if a property exists in the object map
 | 
			
		||||
("z" in y) == false;
 | 
			
		||||
 | 
			
		||||
ts.obj = y;             // object maps can be assigned completely (by value copy)
 | 
			
		||||
let foo = ts.list.a;
 | 
			
		||||
foo == 42;
 | 
			
		||||
 | 
			
		||||
let foo = #{ a:1, };    // trailing comma is OK
 | 
			
		||||
 | 
			
		||||
let foo = #{ a:1, b:2, c:3 }["a"];
 | 
			
		||||
let foo = #{ a:1, b:2, c:3 }.a;
 | 
			
		||||
foo == 1;
 | 
			
		||||
 | 
			
		||||
fn abc() {
 | 
			
		||||
    #{ a:1, b:2, c:3 }  // a function returning an object map
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let foo = abc().b;
 | 
			
		||||
foo == 2;
 | 
			
		||||
 | 
			
		||||
let foo = y["a"];
 | 
			
		||||
foo == 42;
 | 
			
		||||
 | 
			
		||||
y.contains("a") == true;
 | 
			
		||||
y.contains("xyz") == false;
 | 
			
		||||
 | 
			
		||||
y.xyz == ();            // a non-existent property returns '()'
 | 
			
		||||
y["xyz"] == ();
 | 
			
		||||
 | 
			
		||||
y.len == ();            // an object map has no property getter function
 | 
			
		||||
y.len() == 3;           // method calls are OK
 | 
			
		||||
 | 
			
		||||
y.remove("a") == 1;     // remove property
 | 
			
		||||
 | 
			
		||||
y.len() == 2;
 | 
			
		||||
y.contains("a") == false;
 | 
			
		||||
 | 
			
		||||
for name in y.keys() {  // get an array of all the property names via 'keys'
 | 
			
		||||
    print(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for val in y.values() { // get an array of all the property values via 'values'
 | 
			
		||||
    print(val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
y.clear();              // empty the object map
 | 
			
		||||
 | 
			
		||||
y.len() == 0;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Special Support for OOP
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
Object maps can be used to simulate object-oriented programming (OOP) by storing data as properties
 | 
			
		||||
and methods as properties holding [function pointers](fn-ptr.md).
 | 
			
		||||
 | 
			
		||||
If an object map's property holds a [function pointer](fn-ptr.md), the property can simply be called
 | 
			
		||||
like a normal method in method-call syntax.
 | 
			
		||||
 | 
			
		||||
This is a _short-hand_ to avoid the more verbose syntax of using the `call` function keyword.
 | 
			
		||||
 | 
			
		||||
When a property holding a [function pointer](fn-ptr.md) or a [closure](fn-closure.md) is called like
 | 
			
		||||
a method, it is replaced as a method call on the object map itself.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let obj = #{
 | 
			
		||||
                data: 40,
 | 
			
		||||
                action: || this.data += x    // 'action' holds a closure
 | 
			
		||||
           };
 | 
			
		||||
 | 
			
		||||
obj.action(2);                               // calls the function pointer with 'this' bound to 'obj'
 | 
			
		||||
 | 
			
		||||
obj.call(obj.action, 2);                     // <- the above de-sugars to this
 | 
			
		||||
 | 
			
		||||
obj.data == 42;
 | 
			
		||||
 | 
			
		||||
// To achieve the above with normal function pointer call will fail.
 | 
			
		||||
 | 
			
		||||
fn do_action(map, x) { map.data += x; }      // 'map' is a copy
 | 
			
		||||
 | 
			
		||||
obj.action = do_action;                      // <- de-sugars to 'Fn("do_action")'
 | 
			
		||||
 | 
			
		||||
obj.action.call(obj, 2);                     // a copy of 'obj' is passed by value
 | 
			
		||||
 | 
			
		||||
obj.data == 42;                              // 'obj.data' is not changed
 | 
			
		||||
```
 | 
			
		||||
@@ -1,253 +0,0 @@
 | 
			
		||||
Comparison Operators
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
| Operator | Description<br/>(`x` _operator_ `y`) | `x`, `y` same type or are numeric | `x`, `y` different types |
 | 
			
		||||
| :------: | ------------------------------------ | :-------------------------------: | :----------------------: |
 | 
			
		||||
|   `==`   | `x` is equals to `y`                 |       error if not defined        |  `false` if not defined  |
 | 
			
		||||
|   `!=`   | `x` is not equals to `y`             |       error if not defined        |  `true` if not defined   |
 | 
			
		||||
|   `>`    | `x` is greater than `y`              |       error if not defined        |  `false` if not defined  |
 | 
			
		||||
|   `>=`   | `x` is greater than or equals to `y` |       error if not defined        |  `false` if not defined  |
 | 
			
		||||
|   `<`    | `x` is less than `y`                 |       error if not defined        |  `false` if not defined  |
 | 
			
		||||
|   `<=`   | `x` is less than or equals to `y`    |       error if not defined        |  `false` if not defined  |
 | 
			
		||||
 | 
			
		||||
Comparison operators between most values of the same type are built in for all [standard types](values-and-types.md).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Floating-point numbers interoperate with integers
 | 
			
		||||
 | 
			
		||||
Comparing a floating-point number with an integer is also supported.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
42 == 42.0;         // true
 | 
			
		||||
 | 
			
		||||
42.0 == 42;         // true
 | 
			
		||||
 | 
			
		||||
42.0 > 42;          // false
 | 
			
		||||
 | 
			
		||||
42 >= 42.0;         // true
 | 
			
		||||
 | 
			
		||||
42.0 < 42;          // false
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Decimal numbers interoperate with integers
 | 
			
		||||
 | 
			
		||||
Comparing a decimal number with an integer is also supported.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let d = parse_decimal("42");
 | 
			
		||||
 | 
			
		||||
42 == d;            // true
 | 
			
		||||
 | 
			
		||||
d == 42;            // true
 | 
			
		||||
 | 
			
		||||
d > 42;             // false
 | 
			
		||||
 | 
			
		||||
42 >= d;            // true
 | 
			
		||||
 | 
			
		||||
d < 42;             // false
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Strings interoperate with characters
 | 
			
		||||
 | 
			
		||||
Comparing a [string](strings-chars.md) with a [character](strings-chars.md) is also supported, with
 | 
			
		||||
the character first turned into a [string](strings-chars.md) before performing the comparison.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
'x' == "x";         // true
 | 
			
		||||
 | 
			
		||||
"" < 'a';           // true
 | 
			
		||||
 | 
			
		||||
'x' > "hello";      // false
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Comparing different types defaults to `false`
 | 
			
		||||
 | 
			
		||||
Comparing two values of _different_ data types defaults to `false` unless the appropriate operator
 | 
			
		||||
functions have been registered.
 | 
			
		||||
 | 
			
		||||
The exception is `!=` (not equals) which defaults to `true`. This is in line with intuition.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
42 > "42";          // false: i64 cannot be compared with string
 | 
			
		||||
 | 
			
		||||
42 <= "42";         // false: i64 cannot be compared with string
 | 
			
		||||
 | 
			
		||||
let ts = new_ts();  // custom type
 | 
			
		||||
 | 
			
		||||
ts == 42;           // false: different types cannot be compared
 | 
			
		||||
 | 
			
		||||
ts != 42;           // true: different types cannot be compared
 | 
			
		||||
 | 
			
		||||
ts == ts;           // error: '==' not defined for the custom type
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Safety valve: Comparing different _numeric_ types has no default
 | 
			
		||||
 | 
			
		||||
Beware that the above default does _NOT_ apply to numeric values of different types
 | 
			
		||||
(e.g. comparison between `i64` and `u16`, `i32` and `f64`) – when multiple numeric types are
 | 
			
		||||
used it is too easy to mess up and for subtle errors to creep in.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
// Assume variable 'x' = 42_u16, 'y' = 42_u16 (both types of u16)
 | 
			
		||||
 | 
			
		||||
x == y;             // true: '==' operator for u16 is built-in
 | 
			
		||||
 | 
			
		||||
x == "hello";       // false: different non-numeric operand types default to false
 | 
			
		||||
 | 
			
		||||
x == 42;            // error: ==(u16, i64) not defined, no default for numeric types
 | 
			
		||||
 | 
			
		||||
42 == y;            // error: ==(i64, u16) not defined, no default for numeric types
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Boolean Operators
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
```admonish note.side
 | 
			
		||||
 | 
			
		||||
All boolean operators are [built in](../engine/builtin.md) for the `bool` data type.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
|     Operator      | Description | Arity  | Short-circuits? |
 | 
			
		||||
| :---------------: | :---------: | :----: | :-------------: |
 | 
			
		||||
|  `!` _(prefix)_   |    _NOT_    | unary  |       no        |
 | 
			
		||||
|       `&&`        |    _AND_    | binary |     **yes**     |
 | 
			
		||||
|        `&`        |    _AND_    | binary |       no        |
 | 
			
		||||
| <code>\|\|</code> |    _OR_     | binary |     **yes**     |
 | 
			
		||||
|  <code>\|</code>  |    _OR_     | binary |       no        |
 | 
			
		||||
 | 
			
		||||
Double boolean operators `&&` and `||` _short-circuit_ – meaning that the second operand will not be evaluated
 | 
			
		||||
if the first one already proves the condition wrong.
 | 
			
		||||
 | 
			
		||||
Single boolean operators `&` and `|` always evaluate both operands.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
a() || b();         // b() is not evaluated if a() is true
 | 
			
		||||
 | 
			
		||||
a() && b();         // b() is not evaluated if a() is false
 | 
			
		||||
 | 
			
		||||
a() | b();          // both a() and b() are evaluated
 | 
			
		||||
 | 
			
		||||
a() & b();          // both a() and b() are evaluated
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Null-Coalescing Operator
 | 
			
		||||
========================
 | 
			
		||||
 | 
			
		||||
| Operator |  Description  | Arity  | Short-circuits? |
 | 
			
		||||
| :------: | :-----------: | :----: | :-------------: |
 | 
			
		||||
|   `??`   | Null-coalesce | binary |       yes       |
 | 
			
		||||
 | 
			
		||||
The null-coalescing operator (`??`) returns the first operand if it is not `()`, or the second
 | 
			
		||||
operand if the first operand is `()`.
 | 
			
		||||
 | 
			
		||||
This operator _short-circuits_  – meaning that the second operand will not be evaluated if the
 | 
			
		||||
first operand is not `()`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
a ?? b              // returns 'a' if it is not (), otherwise 'b'
 | 
			
		||||
 | 
			
		||||
a() ?? b();         // b() is only evaluated if a() is ()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.small "Tip: Default value for object map property"
 | 
			
		||||
 | 
			
		||||
Use the null-coalescing operator to implement default values for non-existent
 | 
			
		||||
[object map](object-maps.md) properties.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let map = #{ foo: 42 };
 | 
			
		||||
 | 
			
		||||
// Regular property access
 | 
			
		||||
let x = map.foo;            // x == 42
 | 
			
		||||
 | 
			
		||||
// Non-existent property
 | 
			
		||||
let x = map.bar;            // x == ()
 | 
			
		||||
 | 
			
		||||
// Default value for property
 | 
			
		||||
let x = map.bar ?? 42;      // x == 42
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
Short-circuit loops and early returns
 | 
			
		||||
-------------------------------------
 | 
			
		||||
 | 
			
		||||
The following statements are allowed to follow the null-coalescing operator:
 | 
			
		||||
 | 
			
		||||
* `break`
 | 
			
		||||
* `continue`
 | 
			
		||||
* [`return`](return.md)
 | 
			
		||||
* [`throw`](throw.md)
 | 
			
		||||
 | 
			
		||||
This means that you can use the null-coalescing operator to short-circuit loops and/or
 | 
			
		||||
early-return from functions when the value tested is `()`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let total = 0;
 | 
			
		||||
 | 
			
		||||
for value in list {
 | 
			
		||||
    // Whenever 'calculate' returns '()', the loop stops
 | 
			
		||||
    total += calculate(value) ?? break;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
In Operator
 | 
			
		||||
===========
 | 
			
		||||
 | 
			
		||||
```admonish question.side "Trivia"
 | 
			
		||||
 | 
			
		||||
The `in` operator is simply syntactic sugar for a call to the `contains` function.
 | 
			
		||||
 | 
			
		||||
Similarly, `!in` is a call to `!contains`.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `in` operator is used to check for _containment_ – i.e. whether a particular collection
 | 
			
		||||
data type _contains_ a particular item.
 | 
			
		||||
 | 
			
		||||
Similarly, `!in` is used to check for non-existence – i.e. it is `true` if a particular
 | 
			
		||||
collection data type does _not_ contain a particular item.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
42 in array;
 | 
			
		||||
 | 
			
		||||
array.contains(42);     // <- the above is equivalent to this
 | 
			
		||||
 | 
			
		||||
123 !in array;
 | 
			
		||||
 | 
			
		||||
!array.contains(123);   // <- the above is equivalent to this
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Built-in support for standard data types
 | 
			
		||||
 | 
			
		||||
|          Data type           |                            Check for                            |
 | 
			
		||||
| :--------------------------: | :-------------------------------------------------------------: |
 | 
			
		||||
|  Numeric [range](ranges.md)  |                         integer number                          |
 | 
			
		||||
|      [Array](arrays.md)      |                         contained item                          |
 | 
			
		||||
| [Object map](object-maps.md) |                          property name                          |
 | 
			
		||||
|  [String](strings-chars.md)  | [sub-string](strings-chars.md) or [character](strings-chars.md) |
 | 
			
		||||
 | 
			
		||||
### Examples
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let array = [1, "abc", 42, ()];
 | 
			
		||||
 | 
			
		||||
42 in array == true;                // check array for item
 | 
			
		||||
 | 
			
		||||
let map = #{
 | 
			
		||||
    foo: 42,
 | 
			
		||||
    bar: true,
 | 
			
		||||
    baz: "hello"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
"foo" in map == true;               // check object map for property name
 | 
			
		||||
 | 
			
		||||
'w' in "hello, world!" == true;     // check string for character
 | 
			
		||||
 | 
			
		||||
'w' !in "hello, world!" == false;
 | 
			
		||||
 | 
			
		||||
"wor" in "hello, world" == true;    // check string for sub-string
 | 
			
		||||
 | 
			
		||||
42 in -100..100 == true;            // check range for number
 | 
			
		||||
```
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
Function Overloading
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
{{#title Function Overloading}}
 | 
			
		||||
 | 
			
		||||
[Functions](functions.md) defined in script can be _overloaded_ by _arity_ (i.e. they are resolved
 | 
			
		||||
purely upon the function's _name_ and _number_ of parameters, but not parameter _types_ since all
 | 
			
		||||
parameters are dynamic).
 | 
			
		||||
 | 
			
		||||
New definitions _overwrite_ previous definitions of the same name and number of parameters.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
fn foo(x, y, z) {
 | 
			
		||||
    print(`Three!!! ${x}, ${y}, ${z}`);
 | 
			
		||||
}
 | 
			
		||||
fn foo(x) {
 | 
			
		||||
    print(`One! ${x}`);
 | 
			
		||||
}
 | 
			
		||||
fn foo(x, y) {
 | 
			
		||||
    print(`Two! ${x}, ${y}`);
 | 
			
		||||
}
 | 
			
		||||
fn foo() {
 | 
			
		||||
    print("None.");
 | 
			
		||||
}
 | 
			
		||||
fn foo(x) {     // <- overwrites previous definition
 | 
			
		||||
    print(`HA! NEW ONE! ${x}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
foo(1,2,3);     // prints "Three!!! 1,2,3"
 | 
			
		||||
 | 
			
		||||
foo(42);        // prints "HA! NEW ONE! 42"
 | 
			
		||||
 | 
			
		||||
foo(1,2);       // prints "Two!! 1,2"
 | 
			
		||||
 | 
			
		||||
foo();          // prints "None."
 | 
			
		||||
```
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
`print` and `debug`
 | 
			
		||||
===================
 | 
			
		||||
 | 
			
		||||
The `print` and `debug` functions can be used to output values.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
print("hello");         // prints "hello" to stdout
 | 
			
		||||
 | 
			
		||||
print(1 + 2 + 3);       // prints "6" to stdout
 | 
			
		||||
 | 
			
		||||
let x = 42;
 | 
			
		||||
 | 
			
		||||
print(`hello${x}`);     // prints "hello42" to stdout
 | 
			
		||||
 | 
			
		||||
debug("world!");        // prints "world!" to stdout using debug formatting
 | 
			
		||||
```
 | 
			
		||||
@@ -1,94 +0,0 @@
 | 
			
		||||
Ranges
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Syntax
 | 
			
		||||
------
 | 
			
		||||
 | 
			
		||||
Numeric ranges can be constructed by the `..` (exclusive) or `..=` (inclusive) operators.
 | 
			
		||||
 | 
			
		||||
### Exclusive range
 | 
			
		||||
 | 
			
		||||
> _start_ `..` _end_
 | 
			
		||||
 | 
			
		||||
An _exclusive_ range does not include the last (i.e. "end") value.
 | 
			
		||||
 | 
			
		||||
[`type_of()`](type-of.md) an exclusive range returns `"range"`.
 | 
			
		||||
 | 
			
		||||
### Inclusive range
 | 
			
		||||
 | 
			
		||||
> _start_ `..=` _end_
 | 
			
		||||
 | 
			
		||||
An _inclusive_ range includes the last (i.e. "end") value.
 | 
			
		||||
 | 
			
		||||
[`type_of()`](type-of.md) an inclusive range returns `"range="`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Usage Scenarios
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Ranges are commonly used in the following scenarios.
 | 
			
		||||
 | 
			
		||||
| Scenario                                     | Example                                 |
 | 
			
		||||
| -------------------------------------------- | --------------------------------------- |
 | 
			
		||||
| [`for`](for.md) statements                   | `for n in 0..100 { ... }`               |
 | 
			
		||||
| [`in`](operators.md) expressions             | `if n in 0..100 { ... }`                |
 | 
			
		||||
| [`switch`](switch.md) expressions            | `switch n { 0..100 => ... }`            |
 | 
			
		||||
| [Bit-fields](bit-fields.md) access           | `let x = n[2..6];`                      |
 | 
			
		||||
| Bits iteration                               | `for bit in n.bits(2..=9) { ... }`      |
 | 
			
		||||
| [Array](arrays.md) range-based APIs          | `array.extract(2..8)`                   |
 | 
			
		||||
| [BLOB](blobs.md) range-based APIs            | `blob.parse_le_int(4..8)`               |
 | 
			
		||||
| [String](strings-chars.md) range-based APIs  | `string.sub_string(4..=12)`             |
 | 
			
		||||
| [Characters](strings-chars.md) iteration     | `for ch in string.bits(4..=12) { ... }` |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Built-in Functions
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
The following methods operate on ranges.
 | 
			
		||||
 | 
			
		||||
| Function                           |  Parameter(s)   | Description                                   |
 | 
			
		||||
| ---------------------------------- | :-------------: | --------------------------------------------- |
 | 
			
		||||
| `start` method and property        |                 | beginning of the range                        |
 | 
			
		||||
| `end` method and property          |                 | end of the range                              |
 | 
			
		||||
| `contains`, `in` operator          | number to check | does this range contain the specified number? |
 | 
			
		||||
| `is_empty` method and property     |                 | returns `true` if the range contains no items |
 | 
			
		||||
| `is_inclusive` method and property |                 | is the range inclusive?                       |
 | 
			
		||||
| `is_exclusive` method and property |                 | is the range exclusive?                       |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TL;DR
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
```admonish question "What happened to the _open-ended_ ranges?"
 | 
			
		||||
 | 
			
		||||
Rust has _open-ended_ ranges, such as `start..`, `..end` and `..=end`.  They are not available in Rhai.
 | 
			
		||||
 | 
			
		||||
They are not needed because Rhai can overload functions.
 | 
			
		||||
 | 
			
		||||
Typically, an API accepting ranges as parameters would have equivalent versions that accept a
 | 
			
		||||
starting position and a length (the standard `start + len` pair), as well as a versions that accept
 | 
			
		||||
only the starting position (the length assuming to the end).
 | 
			
		||||
 | 
			
		||||
In fact, usually all versions redirect to a call to one single version.
 | 
			
		||||
 | 
			
		||||
Therefore, there should always be a function that can do what open-ended ranges are intended for.
 | 
			
		||||
 | 
			
		||||
The left-open form (i.e. `..end` and `..=end`) is trivially replaced by using zero as the starting
 | 
			
		||||
position with a length that corresponds to the end position (for `..end`).
 | 
			
		||||
 | 
			
		||||
The right-open form (i.e. `start..`) is trivially replaced by the version taking a single starting position.
 | 
			
		||||
 | 
			
		||||
~~~rust
 | 
			
		||||
let x = [1, 2, 3, 4, 5];
 | 
			
		||||
 | 
			
		||||
x.extract(0..3);    // normal range argument
 | 
			
		||||
                    // copies 'x' from positions 0-2
 | 
			
		||||
 | 
			
		||||
x.extract(2);       // copies 'x' from position 2 onwards
 | 
			
		||||
                    // equivalent to '2..'
 | 
			
		||||
 | 
			
		||||
x.extract(0, 2);    // copies 'x' from beginning for 2 items
 | 
			
		||||
                    // equivalent to '..2'
 | 
			
		||||
~~~
 | 
			
		||||
```
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
Return Value
 | 
			
		||||
============
 | 
			
		||||
 | 
			
		||||
`return`
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
The `return` statement is used to immediately stop evaluation and exist the current context
 | 
			
		||||
(typically a [function](functions.md) call) yielding a _return value_.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
return;             // equivalent to return ();
 | 
			
		||||
 | 
			
		||||
return 123 + 456;   // returns 579
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A `return` statement at _global_ level exits the script with the return value as the result.
 | 
			
		||||
 | 
			
		||||
A `return` statement inside a [function call](functions.md) exits with a return value to the caller.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
`exit`
 | 
			
		||||
------
 | 
			
		||||
 | 
			
		||||
Similar to the `return` statement, the `exit` _function_ is used to immediately stop evaluation,
 | 
			
		||||
but it does so regardless of where it is called from, even deep inside nested function calls.
 | 
			
		||||
 | 
			
		||||
The result value of `exit`, when omitted, defaults to `()`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
fn foo() {
 | 
			
		||||
    exit(42);       // exit with result value 42
 | 
			
		||||
}
 | 
			
		||||
fn bar() {
 | 
			
		||||
    foo();
 | 
			
		||||
}
 | 
			
		||||
fn baz() {
 | 
			
		||||
    bar();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let x = baz();      // exits with result value 42
 | 
			
		||||
 | 
			
		||||
print(x);           // <- this is never run
 | 
			
		||||
```
 | 
			
		||||
@@ -1,121 +0,0 @@
 | 
			
		||||
Statements
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
Statements are terminated by semicolons `;` and they are mandatory, except for the _last_ statement
 | 
			
		||||
in a _block_ (enclosed by `{` ... `}` pairs) where it can be omitted.
 | 
			
		||||
 | 
			
		||||
Semicolons can also be omitted for statement types that always end in a block – for example
 | 
			
		||||
the [`if`](if.md), [`while`](while.md), [`for`](for.md),  [`loop`](loop.md) and
 | 
			
		||||
[`switch`](switch.md) statements.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let a = 42;             // normal assignment statement
 | 
			
		||||
let a = foo(42);        // normal function call statement
 | 
			
		||||
foo < 42;               // normal expression as statement
 | 
			
		||||
 | 
			
		||||
let a = { 40 + 2 };     // 'a' is set to the value of the statements block, which is the value of the last statement
 | 
			
		||||
//              ^ the last statement does not require a terminating semicolon (but also works with it)
 | 
			
		||||
//                ^ semicolon required here to terminate the 'let' statement
 | 
			
		||||
//                  it is a syntax error without it, even though it ends with '}'
 | 
			
		||||
//                  that is because the 'let' statement doesn't end in a block
 | 
			
		||||
 | 
			
		||||
if foo { a = 42 }
 | 
			
		||||
//               ^ no need to terminate an if-statement with a semicolon
 | 
			
		||||
//                 that is because the 'if' statement ends in a block
 | 
			
		||||
 | 
			
		||||
4 * 10 + 2              // a statement which is just one expression - no ending semicolon is OK
 | 
			
		||||
                        // because it is the last statement of the whole block
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Statements Block
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
### Syntax
 | 
			
		||||
 | 
			
		||||
Statements blocks in Rhai are formed by enclosing zero or more statements within braces `{`...`}`.
 | 
			
		||||
 | 
			
		||||
> `{` _statement_`;` _statement_`;` ... _statement_ `}`
 | 
			
		||||
>
 | 
			
		||||
> `{` _statement_`;` _statement_`;` ... _statement_`;` `}`      `// trailing semi-colon is optional`
 | 
			
		||||
 | 
			
		||||
### Closed scope
 | 
			
		||||
 | 
			
		||||
A statements block forms a _closed_ scope.
 | 
			
		||||
 | 
			
		||||
Any [variable](variable.md) and/or [constant](constant.md) defined within the block are removed
 | 
			
		||||
outside the block, so are [modules](modules/index.md) [imported](modules/import.md) within the block.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 42;
 | 
			
		||||
let y = 18;
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    import "hello" as h;
 | 
			
		||||
    const HELLO = 99;
 | 
			
		||||
    let y = 0;
 | 
			
		||||
 | 
			
		||||
    h::greet();         // ok
 | 
			
		||||
 | 
			
		||||
    print(y + HELLO);   // prints 99 (y is zero)
 | 
			
		||||
 | 
			
		||||
        :    
 | 
			
		||||
        :    
 | 
			
		||||
}                       // <- 'HELLO' and 'y' go away here...
 | 
			
		||||
 | 
			
		||||
print(x + y);           // prints 60 (y is still 18)
 | 
			
		||||
 | 
			
		||||
print(HELLO);           // <- error: 'HELLO' not found
 | 
			
		||||
 | 
			
		||||
h::greet();             // <- error: module 'h' not found
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Statement Expression
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
A statement can be used anywhere where an _expression_ is expected.
 | 
			
		||||
 | 
			
		||||
These are called, for lack of a more creative name, "statement expressions."
 | 
			
		||||
 | 
			
		||||
The _last_ statement of a statements block is _always_ the block's return value when used as a statement,
 | 
			
		||||
_regardless_ of whether it is terminated by a semicolon or not.
 | 
			
		||||
 | 
			
		||||
If the last statement has no return value (e.g. variable definitions, assignments) then it is
 | 
			
		||||
assumed to be `()`.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = {
 | 
			
		||||
    let foo = calc_something();
 | 
			
		||||
    let bar = foo + baz;
 | 
			
		||||
    bar.further_processing();       // <- this is the return value
 | 
			
		||||
};                                  // <- semicolon is needed here...
 | 
			
		||||
 | 
			
		||||
// The above is equivalent to:
 | 
			
		||||
let result;
 | 
			
		||||
{
 | 
			
		||||
    let foo = calc_something();
 | 
			
		||||
    let bar = foo + baz;
 | 
			
		||||
    result = bar.further_processing();
 | 
			
		||||
}
 | 
			
		||||
let x = result;
 | 
			
		||||
 | 
			
		||||
// Statement expressions can be inserted inside normal expressions
 | 
			
		||||
// to avoid duplicated calculations
 | 
			
		||||
let x = foo(bar) + { let v = calc(); process(v, v.len, v.abs) } + baz;
 | 
			
		||||
 | 
			
		||||
// The above is equivalent to:
 | 
			
		||||
let foo_result = foo(bar);
 | 
			
		||||
let calc_result;
 | 
			
		||||
{
 | 
			
		||||
    let v = calc();
 | 
			
		||||
    result = process(v, v.len, v.abs);  // <- avoid calculating 'v'
 | 
			
		||||
}
 | 
			
		||||
let x = foo_result + calc_result + baz;
 | 
			
		||||
 | 
			
		||||
// Statement expressions are also useful as function call arguments
 | 
			
		||||
// when side effects are desired
 | 
			
		||||
do_work(x, y, { let z = foo(x, y); print(z); z });
 | 
			
		||||
           // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
           //       statement expression
 | 
			
		||||
```
 | 
			
		||||
@@ -1,149 +0,0 @@
 | 
			
		||||
Standard String Functions
 | 
			
		||||
=========================
 | 
			
		||||
 | 
			
		||||
{{#title Standard String Functions and Operators}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The following standard methods operate on [strings](strings-chars.md) (and possibly characters).
 | 
			
		||||
 | 
			
		||||
| Function                       | Parameter(s)                                                                                                                                    | Description                                                                                                                                                |
 | 
			
		||||
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `len` method and property      | _none_                                                                                                                                          | returns the number of characters (**not** number of bytes) in the string                                                                                   |
 | 
			
		||||
| `bytes` method and property    | _none_                                                                                                                                          | returns the number of bytes making up the UTF-8 string; for strings containing only ASCII characters, this is much faster than `len`                       |
 | 
			
		||||
| `is_empty` method and property | _none_                                                                                                                                          | returns `true` if the string is empty                                                                                                                      |
 | 
			
		||||
| `to_blob`                      | _none_                                                                                                                                          | converts the string into an UTF-8 encoded byte-stream and returns it as a [BLOB](blobs.md).                                                                |
 | 
			
		||||
| `to_chars`                     | _none_                                                                                                                                          | splits the string by individual characters, returning them as an [array](arrays.md)                                                                        |
 | 
			
		||||
| `get`                          | position, counting from end if < 0                                                                                                              | gets the character at a certain position (`()` if the position is not valid)                                                                               |
 | 
			
		||||
| `set`                          | <ol><li>position, counting from end if < 0</li><li>new character</li></ol>                                                                      | sets a certain position to a new character (no effect if the position is not valid)                                                                        |
 | 
			
		||||
| `pad`                          | <ol><li>target length</li><li>character/string to pad</li></ol>                                                                                 | pads the string with a character or a string to at least a specified length                                                                                |
 | 
			
		||||
| `append`, `+=` operator        | item to append                                                                                                                                  | adds the display text of an item to the end of the string                                                                                                  |
 | 
			
		||||
| `remove`                       | character/string to remove                                                                                                                      | removes a character or a string from the string                                                                                                            |
 | 
			
		||||
| `pop`                          | _(optional)_ number of characters to remove, none if ≤ 0, entire string if ≥ length                                                             | removes the last character (if no parameter) and returns it (`()` if empty); otherwise, removes the last number of characters and returns them as a string |
 | 
			
		||||
| `clear`                        | _none_                                                                                                                                          | empties the string                                                                                                                                         |
 | 
			
		||||
| `truncate`                     | target length                                                                                                                                   | cuts off the string at exactly a specified number of characters                                                                                            |
 | 
			
		||||
| `to_upper`                     | _none_                                                                                                                                          | converts the string/character into upper-case as a new string/character and returns it                                                                     |
 | 
			
		||||
| `to_lower`                     | _none_                                                                                                                                          | converts the string/character into lower-case as a new string/character and returns it                                                                     |
 | 
			
		||||
| `make_upper`                   | _none_                                                                                                                                          | converts the string/character into upper-case                                                                                                              |
 | 
			
		||||
| `make_lower`                   | _none_                                                                                                                                          | converts the string/character into lower-case                                                                                                              |
 | 
			
		||||
| `trim`                         | _none_                                                                                                                                          | trims the string of whitespace at the beginning and end                                                                                                    |
 | 
			
		||||
| `contains`                     | character/sub-string to search for                                                                                                              | checks if a certain character or sub-string occurs in the string                                                                                           |
 | 
			
		||||
| `starts_with`                  | string                                                                                                                                          | returns `true` if the string starts with a certain string                                                                                                  |
 | 
			
		||||
| `ends_with`                    | string                                                                                                                                          | returns `true` if the string ends with a certain string                                                                                                    |
 | 
			
		||||
| `min`                          | <ol><li>first character/string</li><li>second character/string</li><ol>                                                                         | returns the smaller of two characters/strings                                                                                                              |
 | 
			
		||||
| `max`                          | <ol><li>first character/string</li><li>second character/string</li><ol>                                                                         | returns the larger of two characters/strings                                                                                                               |
 | 
			
		||||
| `index_of`                     | <ol><li>character/sub-string to search for</li><li>_(optional)_ start position, counting from end if < 0, end if ≥ length</li></ol>             | returns the position that a certain character or sub-string occurs in the string, or −1 if not found                                                       |
 | 
			
		||||
| `sub_string`                   | <ol><li>start position, counting from end if < 0</li><li>_(optional)_ number of characters to extract, none if ≤ 0, to end if omitted</li></ol> | extracts a sub-string                                                                                                                                      |
 | 
			
		||||
| `sub_string`                   | [range](ranges.md) of characters to extract, from beginning if ≤ 0, to end if ≥ length                                                          | extracts a sub-string                                                                                                                                      |
 | 
			
		||||
| `split`                        | _none_                                                                                                                                          | splits the string by whitespaces, returning an [array](arrays.md) of string segments                                                                       |
 | 
			
		||||
| `split`                        | position to split at (in number of characters), counting from end if < 0, end if ≥ length                                                       | splits the string into two segments at the specified character position, returning an [array](arrays) of two string segments                               |
 | 
			
		||||
| `split`                        | <ol><li>delimiter character/string</li><li>_(optional)_ maximum number of segments, 1 if < 1</li></ol>                                          | splits the string by the specified delimiter, returning an [array](arrays) of string segments                                                              |
 | 
			
		||||
| `split_rev`                    | <ol><li>delimiter character/string</li><li>_(optional)_ maximum number of segments, 1 if < 1</li></ol>                                          | splits the string by the specified delimiter in reverse order, returning an [array](arrays) of string segments                                             |
 | 
			
		||||
| `crop`                         | <ol><li>start position, counting from end if < 0</li><li>_(optional)_ number of characters to retain, none if ≤ 0, to end if omitted</li></ol>  | retains only a portion of the string                                                                                                                       |
 | 
			
		||||
| `crop`                         | [range](ranges.md) of characters to retain, from beginning if ≤ 0, to end if ≥ length                                                           | retains only a portion of the string                                                                                                                       |
 | 
			
		||||
| `replace`                      | <ol><li>target character/sub-string</li><li>replacement character/string</li></ol>                                                              | replaces a sub-string with another                                                                                                                         |
 | 
			
		||||
| `chars` method and property    | <ol><li>_(optional)_ start position, counting from end if < 0</li><li>_(optional)_ number of characters to iterate, none if ≤ 0</li></ol>       | allows iteration of the characters inside the string                                                                                                       |
 | 
			
		||||
 | 
			
		||||
Beware that functions that involve indexing into a [string](strings-chars.md) to get at individual
 | 
			
		||||
[characters](strings-chars.md), e.g. `sub_string`, require walking through the entire UTF-8 encoded
 | 
			
		||||
bytes stream to extract individual Unicode characters and counting them, which can be slow for long
 | 
			
		||||
[strings](strings-chars.md).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Building Strings
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
[Strings](strings-chars.md) can be built from segments via the `+` operator.
 | 
			
		||||
 | 
			
		||||
| Operator                             | Description                                                                                                   |
 | 
			
		||||
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| [string](strings-chars.md) `+=` item | convert the item into a [string](strings-chars.md), then append it to the first [string](strings-chars.md)    |
 | 
			
		||||
| [string](strings-chars.md) `+` item  | convert the item into a [string](strings-chars.md), then concatenate them as a new [string](strings-chars.md) |
 | 
			
		||||
| item `+` [string](strings-chars.md)  | convert the item into a [string](strings-chars.md), then concatenate them as a new [string](strings-chars.md) |
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 42;
 | 
			
		||||
 | 
			
		||||
// Build string with '+'
 | 
			
		||||
let s = "The answer is: " + x + "!!!";
 | 
			
		||||
 | 
			
		||||
// Prints: "The answer is: 42!!!"
 | 
			
		||||
print(s);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Standard Operators Between Strings and/or Characters
 | 
			
		||||
 | 
			
		||||
The following standard operators inter-operate between [strings](strings-chars.md) and/or
 | 
			
		||||
[characters](strings-chars.md).
 | 
			
		||||
 | 
			
		||||
When one (or both) of the operands is a [character](strings-chars.md), it is first converted into a
 | 
			
		||||
one-character [string](strings-chars.md) before running the operator.
 | 
			
		||||
 | 
			
		||||
| Operator  | Description                                                                                        |
 | 
			
		||||
| --------- | -------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `+`, `+=` | [character](strings-chars.md)/[string](strings-chars.md) concatenation                             |
 | 
			
		||||
| `-`, `-=` | remove [character](strings-chars.md/[sub-string](strings-chars.md) from [string](strings-chars.md) |
 | 
			
		||||
| `==`      | equals to                                                                                          |
 | 
			
		||||
| `!=`      | not equals to                                                                                      |
 | 
			
		||||
| `>`       | greater than                                                                                       |
 | 
			
		||||
| `>=`      | greater than or equals to                                                                          |
 | 
			
		||||
| `<`       | less than                                                                                          |
 | 
			
		||||
| `<=`      | less than or equals to                                                                             |
 | 
			
		||||
 | 
			
		||||
### Interop with BLOB's
 | 
			
		||||
 | 
			
		||||
For convenience, when a [BLOB](blobs.md) is appended to a [string](strings-chars.md), or vice
 | 
			
		||||
versa, it is treated as a UTF-8 encoded byte stream and automatically first converted into the appropriate
 | 
			
		||||
[string](strings-chars.md) value.
 | 
			
		||||
 | 
			
		||||
That is because it is rarely useful to append a [BLOB](blobs.md) into a string, but extremely useful
 | 
			
		||||
to be able to directly manipulate UTF-8 encoded text.
 | 
			
		||||
 | 
			
		||||
| Operator  | Description                                                                                             |
 | 
			
		||||
| --------- | ------------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| `+`, `+=` | append a [BLOB](blobs.md) (as a UTF-8 encoded byte stream) to the end of the [string](strings-chars.md) |
 | 
			
		||||
| `+`       | concatenate a [BLOB](blobs.md) (as a UTF-8 encoded byte stream) with a [string](strings-chars.md)       |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let full_name == " Bob C. Davis ";
 | 
			
		||||
full_name.len == 14;
 | 
			
		||||
 | 
			
		||||
full_name.trim();
 | 
			
		||||
full_name.len == 12;
 | 
			
		||||
full_name == "Bob C. Davis";
 | 
			
		||||
 | 
			
		||||
full_name.pad(15, '$');
 | 
			
		||||
full_name.len == 15;
 | 
			
		||||
full_name == "Bob C. Davis$$$";
 | 
			
		||||
 | 
			
		||||
let n = full_name.index_of('$');
 | 
			
		||||
n == 12;
 | 
			
		||||
 | 
			
		||||
full_name.index_of("$$", n + 1) == 13;
 | 
			
		||||
 | 
			
		||||
full_name.sub_string(n, 3) == "$$$";
 | 
			
		||||
full_name.sub_string(n..n+3) == "$$$";
 | 
			
		||||
 | 
			
		||||
full_name.truncate(6);
 | 
			
		||||
full_name.len == 6;
 | 
			
		||||
full_name == "Bob C.";
 | 
			
		||||
 | 
			
		||||
full_name.replace("Bob", "John");
 | 
			
		||||
full_name.len == 7;
 | 
			
		||||
full_name == "John C.";
 | 
			
		||||
 | 
			
		||||
full_name.contains('C') == true;
 | 
			
		||||
full_name.contains("John") == true;
 | 
			
		||||
 | 
			
		||||
full_name.crop(5);
 | 
			
		||||
full_name == "C.";
 | 
			
		||||
 | 
			
		||||
full_name.crop(0, 1);
 | 
			
		||||
full_name == "C";
 | 
			
		||||
 | 
			
		||||
full_name.clear();
 | 
			
		||||
full_name.len == 0;
 | 
			
		||||
```
 | 
			
		||||
@@ -1,348 +0,0 @@
 | 
			
		||||
Strings and Characters
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
String in Rhai contain any text sequence of valid Unicode characters.
 | 
			
		||||
 | 
			
		||||
[`type_of()`](type-of.md) a string returns `"string"`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
String and Character Literals
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
String and character literals follow JavaScript-style syntax.
 | 
			
		||||
 | 
			
		||||
| Type                      |     Quotes      | Escapes? | Continuation? | Interpolation? |
 | 
			
		||||
| ------------------------- | :-------------: | :------: | :-----------: | :------------: |
 | 
			
		||||
| Normal string             |     `"..."`     |   yes    |   with `\`    |     **no**     |
 | 
			
		||||
| Raw string                | `#..#"..."#..#` |  **no**  |    **no**     |     **no**     |
 | 
			
		||||
| Multi-line literal string |   `` `...` ``   |  **no**  |    **no**     | with `${...}`  |
 | 
			
		||||
| Character                 |     `'...'`     |   yes    |    **no**     |     **no**     |
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Building strings"
 | 
			
		||||
 | 
			
		||||
Strings can be built up from other strings and types via the `+` or `+=` operators.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Standard Escape Sequences
 | 
			
		||||
-------------------------
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.side "Tip: Character `to_int()`"
 | 
			
		||||
 | 
			
		||||
Use the `to_int` method to convert a Unicode character into its 32-bit Unicode encoding.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
There is built-in support for Unicode (`\u`_xxxx_ or `\U`_xxxxxxxx_) and hex (`\x`_xx_) escape
 | 
			
		||||
sequences for normal strings and characters.
 | 
			
		||||
 | 
			
		||||
Hex sequences map to ASCII characters, while `\u` maps to 16-bit common Unicode code points and `\U`
 | 
			
		||||
maps the full, 32-bit extended Unicode code points.
 | 
			
		||||
 | 
			
		||||
Escape sequences are not supported for multi-line literal strings wrapped by back-ticks (`` ` ``).
 | 
			
		||||
 | 
			
		||||
| Escape sequence | Meaning                          |
 | 
			
		||||
| --------------- | -------------------------------- |
 | 
			
		||||
| `\\`            | back-slash (`\`)                 |
 | 
			
		||||
| `\t`            | tab                              |
 | 
			
		||||
| `\r`            | carriage-return (`CR`)           |
 | 
			
		||||
| `\n`            | line-feed (`LF`)                 |
 | 
			
		||||
| `\"` or `""`    | double-quote (`"`)               |
 | 
			
		||||
| `\'`            | single-quote (`'`)               |
 | 
			
		||||
| `\x`_xx_        | ASCII character in 2-digit hex   |
 | 
			
		||||
| `\u`_xxxx_      | Unicode character in 4-digit hex |
 | 
			
		||||
| `\U`_xxxxxxxx_  | Unicode character in 8-digit hex |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Line Continuation
 | 
			
		||||
-----------------
 | 
			
		||||
 | 
			
		||||
For a normal string wrapped by double-quotes (`"`), a back-slash (`\`) character at the end of a
 | 
			
		||||
line indicates that the string continues onto the next line _without any line-break_.
 | 
			
		||||
 | 
			
		||||
Whitespace up to the indentation of the opening double-quote is ignored in order to enable lining up
 | 
			
		||||
blocks of text.
 | 
			
		||||
 | 
			
		||||
Spaces are _not_ added, so to separate one line with the next with a space, put a space before the
 | 
			
		||||
ending back-slash (`\`) character.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = "hello, world!\
 | 
			
		||||
         hello world again! \
 | 
			
		||||
         this is the ""last"" time!!!";
 | 
			
		||||
// ^^^^^^ these whitespaces are ignored
 | 
			
		||||
 | 
			
		||||
// The above is the same as:
 | 
			
		||||
let x = "hello, world!hello world again! this is the \"last\" time!!!";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A string with continuation does not open up a new line.  To do so, a new-line character must be
 | 
			
		||||
manually inserted at the appropriate position.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = "hello, world!\n\
 | 
			
		||||
         hello world again!\n\
 | 
			
		||||
         this is the last time!!!";
 | 
			
		||||
 | 
			
		||||
// The above is the same as:
 | 
			
		||||
let x = "hello, world!\nhello world again!\nthis is the last time!!!";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish warning.small "No ending quote before the line ends is a syntax error"
 | 
			
		||||
 | 
			
		||||
If the ending double-quote is omitted, it is a syntax error.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = "hello
 | 
			
		||||
# ";
 | 
			
		||||
//            ^ syntax error: unterminated string literal
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
```admonish question.small "Why not go multi-line?"
 | 
			
		||||
 | 
			
		||||
Technically speaking, there is no difficulty in allowing strings to run for multiple lines
 | 
			
		||||
_without_ the continuation back-slash.
 | 
			
		||||
 | 
			
		||||
Rhai forces you to manually mark a continuation with a back-slash because the ending quote is easy to omit.
 | 
			
		||||
Once it happens, the entire remainder of the script would become one giant, multi-line string.
 | 
			
		||||
 | 
			
		||||
This behavior is different from Rust, where string literals can run for multiple lines.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Raw Strings
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
A _raw string_ is any text enclosed by a pair of double-quotes (`"`), wrapped by hash (`#`) characters.
 | 
			
		||||
 | 
			
		||||
The number of hash (`#`) on each side must be the same.
 | 
			
		||||
 | 
			
		||||
Any text inside the double-quotes, as long as it is not a double-quote (`"`) followed by the same
 | 
			
		||||
number of hash (`#`) characters, is simply copied verbatim, _including control codes and/or
 | 
			
		||||
line-breaks_.
 | 
			
		||||
 | 
			
		||||
Raw strings are very useful for embedded regular expressions, file paths, and program code etc.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = #"Hello, I am a raw string! which means that I can contain
 | 
			
		||||
             line-breaks, \ slashes (not escapes), "quotes" and even # characters!"#
 | 
			
		||||
 | 
			
		||||
// Use more than one '#' if you happen to have '"###...' inside the string...
 | 
			
		||||
 | 
			
		||||
let x = ###"In Rhai, you can write ##"hello"## as a raw string."###;
 | 
			
		||||
//                                         ^^^ this is not the end of the raw string
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Multi-Line Literal Strings
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
A string wrapped by a pair of back-tick (`` ` ``) characters is interpreted _literally_,
 | 
			
		||||
meaning that every single character that lies between the two back-ticks is taken verbatim.
 | 
			
		||||
This include new-lines, whitespaces, escape characters etc.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = `hello, world! "\t\x42"
 | 
			
		||||
  hello world again! 'x'
 | 
			
		||||
     this is the last time!!! `;
 | 
			
		||||
 | 
			
		||||
// The above is the same as:
 | 
			
		||||
let x = "hello, world! \"\\t\\x42\"\n  hello world again! 'x'\n     this is the last time!!! ";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If a back-tick (`` ` ``) appears at the _end_ of a line, then it is understood that the entire text
 | 
			
		||||
block starts from the _next_ line; the starting new-line character is stripped.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = `
 | 
			
		||||
        hello, world! "\t\x42"
 | 
			
		||||
  hello world again! 'x'
 | 
			
		||||
     this is the last time!!!
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
// The above is the same as:
 | 
			
		||||
let x = "        hello, world! \"\\t\\x42\"\n  hello world again! 'x'\n     this is the last time!!!\n";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To actually put a back-tick (`` ` ``) character inside a multi-line literal string, use two
 | 
			
		||||
back-ticks together (i.e. ` `` `).
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = `I have a quote " as well as a back-tick `` here.`;
 | 
			
		||||
 | 
			
		||||
// The above is the same as:
 | 
			
		||||
let x = "I have a quote \" as well as a back-tick ` here.";
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
String Interpolation
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
~~~admonish question.side.wide "What if I want `${` inside?"
 | 
			
		||||
 | 
			
		||||
🤦 Well, you just _have_ to ask for the impossible, don't you?
 | 
			
		||||
 | 
			
		||||
Currently there is no way to escape `${`.  Build the string in three pieces:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
`Interpolations start with "`
 | 
			
		||||
    + "${"
 | 
			
		||||
    + `" and end with }.`
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
Multi-line literal strings support _string interpolation_ wrapped in `${` ... `}`.
 | 
			
		||||
 | 
			
		||||
Interpolation is not supported for normal string or character literals.
 | 
			
		||||
 | 
			
		||||
`${` ... `}` acts as a statements _block_ and can contain anything that is allowed within a
 | 
			
		||||
statements block, including another interpolated string!
 | 
			
		||||
The last result of the block is taken as the value for interpolation.
 | 
			
		||||
 | 
			
		||||
Rhai uses [`to_string`](convert.md) to convert any value into a string, then physically joins all
 | 
			
		||||
the sub-strings together.
 | 
			
		||||
 | 
			
		||||
For convenience, if any interpolated value is a [BLOB](blobs.md), however, it is automatically treated as a
 | 
			
		||||
UTF-8 encoded string.  That is because it is rarely useful to interpolate a [BLOB](blobs.md) into a string,
 | 
			
		||||
but extremely useful to be able to directly manipulate UTF-8 encoded text.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = 42;
 | 
			
		||||
let y = 123;
 | 
			
		||||
 | 
			
		||||
let s = `x = ${x} and y = ${y}.`;                   // <- interpolated string
 | 
			
		||||
 | 
			
		||||
let s = ("x = " + {x} + " and y = " + {y} + ".");   // <- de-sugars to this
 | 
			
		||||
 | 
			
		||||
s == "x = 42 and y = 123.";
 | 
			
		||||
 | 
			
		||||
let s = `
 | 
			
		||||
Undeniable logic:
 | 
			
		||||
1) Hello, ${let w = `${x} world`; if x > 1 { w += "s" } w}!
 | 
			
		||||
2) If ${y} > ${x} then it is ${y > x}!
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
s == "Undeniable logic:\n1) Hello, 42 worlds!\n2) If 123 > 42 then it is true!\n";
 | 
			
		||||
 | 
			
		||||
let blob = blob(3, 0x21);
 | 
			
		||||
 | 
			
		||||
print(blob);                            // prints [212121]
 | 
			
		||||
 | 
			
		||||
print(`Data: ${blob}`);                 // prints "Data: !!!"
 | 
			
		||||
                                        // BLOB is treated as UTF-8 encoded string
 | 
			
		||||
 | 
			
		||||
print(`Data: ${blob.to_string()}`);     // prints "Data: [212121]"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Indexing
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
Strings can be _indexed_ into to get access to any individual character.
 | 
			
		||||
This is similar to many modern languages but different from Rust.
 | 
			
		||||
 | 
			
		||||
### From beginning
 | 
			
		||||
 | 
			
		||||
Individual characters within a string can be accessed with zero-based, non-negative integer indices:
 | 
			
		||||
 | 
			
		||||
> _string_ `[` _index from 0 to (total number of characters − 1)_ `]`
 | 
			
		||||
 | 
			
		||||
### From end
 | 
			
		||||
 | 
			
		||||
A _negative_ index accesses a character in the string counting from the _end_, with −1 being the
 | 
			
		||||
_last_ character.
 | 
			
		||||
 | 
			
		||||
> _string_ `[` _index from −1 to −(total number of characters)_ `]`
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "Character indexing can be SLOOOOOOOOW"
 | 
			
		||||
 | 
			
		||||
Internally, a Rhai string is still stored compactly as a Rust UTF-8 string in order to save memory.
 | 
			
		||||
 | 
			
		||||
Therefore, getting the character at a particular index involves walking through the entire UTF-8
 | 
			
		||||
encoded bytes stream to extract individual Unicode characters, counting them on the way.
 | 
			
		||||
 | 
			
		||||
Because of this, indexing can be a _slow_ procedure, especially for long strings.
 | 
			
		||||
Along the same lines, getting the _length_ of a string (which returns the number of characters, not
 | 
			
		||||
bytes) can also be slow.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Sub-Strings
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
Sub-strings, or _slices_ in some programming languages, are parts of strings.
 | 
			
		||||
 | 
			
		||||
In Rhai, a sub-string can be specified by indexing with a [range](ranges.md) of characters:
 | 
			
		||||
 | 
			
		||||
> _string_ `[` _first character (starting from zero)_ `..` _last character (exclusive)_ `]`
 | 
			
		||||
>
 | 
			
		||||
> _string_ `[` _first character (starting from zero)_ `..=` _last character (inclusive)_ `]`
 | 
			
		||||
 | 
			
		||||
Sub-string [ranges](ranges.md) always start from zero counting towards the end of the string.
 | 
			
		||||
Negative [ranges](ranges.md) are not supported.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let name = "Bob";
 | 
			
		||||
let middle_initial = 'C';
 | 
			
		||||
let last = "Davis";
 | 
			
		||||
 | 
			
		||||
let full_name = `${name} ${middle_initial}. ${last}`;
 | 
			
		||||
full_name == "Bob C. Davis";
 | 
			
		||||
 | 
			
		||||
// String building with different types
 | 
			
		||||
let age = 42;
 | 
			
		||||
let record = `${full_name}: age ${age}`;
 | 
			
		||||
record == "Bob C. Davis: age 42";
 | 
			
		||||
 | 
			
		||||
// Unlike Rust, Rhai strings can be indexed to get a character
 | 
			
		||||
// (disabled with 'no_index')
 | 
			
		||||
let c = record[4];
 | 
			
		||||
c == 'C';                               // single character
 | 
			
		||||
 | 
			
		||||
let slice = record[4..8];               // sub-string slice
 | 
			
		||||
slice == " C. D";
 | 
			
		||||
 | 
			
		||||
ts.s = record;                          // custom type properties can take strings
 | 
			
		||||
 | 
			
		||||
let c = ts.s[4];
 | 
			
		||||
c == 'C';
 | 
			
		||||
 | 
			
		||||
let c = ts.s[-4];                       // negative index counts from the end
 | 
			
		||||
c == 'e';
 | 
			
		||||
 | 
			
		||||
let c = "foo"[0];                       // indexing also works on string literals...
 | 
			
		||||
c == 'f';
 | 
			
		||||
 | 
			
		||||
let c = ("foo" + "bar")[5];             // ... and expressions returning strings
 | 
			
		||||
c == 'r';
 | 
			
		||||
 | 
			
		||||
let text = "hello, world!";
 | 
			
		||||
text[0] = 'H';                          // modify a single character
 | 
			
		||||
text == "Hello, world!";
 | 
			
		||||
 | 
			
		||||
text[7..=11] = "Earth";                 // modify a sub-string slice
 | 
			
		||||
text == "Hello, Earth!";
 | 
			
		||||
 | 
			
		||||
// Escape sequences in strings
 | 
			
		||||
record += " \u2764\n";                  // escape sequence of '❤' in Unicode
 | 
			
		||||
record == "Bob C. Davis: age 42 ❤\n";  // '\n' = new-line
 | 
			
		||||
 | 
			
		||||
// Unlike Rust, Rhai strings can be directly modified character-by-character
 | 
			
		||||
// (disabled with 'no_index')
 | 
			
		||||
record[4] = '\x58'; // 0x58 = 'X'
 | 
			
		||||
record == "Bob X. Davis: age 42 ❤\n";
 | 
			
		||||
 | 
			
		||||
// Use 'in' to test if a substring (or character) exists in a string
 | 
			
		||||
"Davis" in record == true;
 | 
			
		||||
'X' in record == true;
 | 
			
		||||
'C' in record == false;
 | 
			
		||||
 | 
			
		||||
// Strings can be iterated with a 'for' statement, yielding characters
 | 
			
		||||
for ch in record {
 | 
			
		||||
    print(ch);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,244 +0,0 @@
 | 
			
		||||
Switch Statement
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
The `switch` statement allows matching on [literal](../appendix/literals.md) values.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
switch calc_secret_value(x) {
 | 
			
		||||
    1 => print("It's one!"),
 | 
			
		||||
    2 => {
 | 
			
		||||
        // A statements block instead of a one-line statement
 | 
			
		||||
        print("It's two!");
 | 
			
		||||
        print("Again!");
 | 
			
		||||
    }
 | 
			
		||||
    3 => print("Go!"),
 | 
			
		||||
    // A list of alternatives
 | 
			
		||||
    4 | 5 | 6 => print("Some small number!"),
 | 
			
		||||
    // _ is the default when no case matches. It must be the last case.
 | 
			
		||||
    _ => print(`Oops! Something's wrong: ${x}`)
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Default Case
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
A _default_ case (i.e. when no other cases match) can be specified with `_`.
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "Must be last"
 | 
			
		||||
 | 
			
		||||
The default case must be the _last_ case in the `switch` statement.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
switch wrong_default {
 | 
			
		||||
    1 => 2,
 | 
			
		||||
    _ => 9,     // <- syntax error: default case not the last
 | 
			
		||||
    2 => 3,
 | 
			
		||||
    3 => 4,     // <- ending with extra comma is OK
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
switch wrong_default {
 | 
			
		||||
    1 => 2,
 | 
			
		||||
    2 => 3,
 | 
			
		||||
    3 => 4,
 | 
			
		||||
    _ => 8,     // <- syntax error: default case not the last
 | 
			
		||||
    _ => 9
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Array and Object Map Literals Also Work
 | 
			
		||||
---------------------------------------
 | 
			
		||||
 | 
			
		||||
The `switch` expression can match against any _[literal](../appendix/literals.md)_, including
 | 
			
		||||
[array](arrays.md) and [object map](object-maps.md) [literals](../appendix/literals.md).
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// Match on arrays
 | 
			
		||||
switch [foo, bar, baz] {
 | 
			
		||||
    ["hello", 42, true] => ...,
 | 
			
		||||
    ["hello", 123, false] => ...,
 | 
			
		||||
    ["world", 1, true] => ...,
 | 
			
		||||
    _ => ...
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Match on object maps
 | 
			
		||||
switch map {
 | 
			
		||||
    #{ a: 1, b: 2, c: true } => ...,
 | 
			
		||||
    #{ a: 42, d: "hello" } => ...,
 | 
			
		||||
    _ => ...
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Case Conditions
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
Similar to Rust, each case (except the default case at the end) can provide an optional condition
 | 
			
		||||
that must evaluate to `true` in order for the case to match.
 | 
			
		||||
 | 
			
		||||
All cases are checked in order, so an earlier case that matches will override all later cases.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let result = switch calc_secret_value(x) {
 | 
			
		||||
    1 if some_external_condition(x, y, z) => 100,
 | 
			
		||||
 | 
			
		||||
    1 | 2 | 3 if x < foo => 200,    // <- all alternatives share the same condition
 | 
			
		||||
    
 | 
			
		||||
    2 if bar() => 999,
 | 
			
		||||
 | 
			
		||||
    2 => "two",                     // <- fallback value for 2
 | 
			
		||||
 | 
			
		||||
    2 => "dead code",               // <- this case is a duplicate and will never match
 | 
			
		||||
                                    //    because the previous case matches first
 | 
			
		||||
 | 
			
		||||
    5 if CONDITION => 123,          // <- value for 5 matching condition
 | 
			
		||||
 | 
			
		||||
    5 => "five",                    // <- fallback value for 5
 | 
			
		||||
 | 
			
		||||
    _ if CONDITION => 8888          // <- syntax error: default case cannot have condition
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish tip "Tip: Use with `type_of()`"
 | 
			
		||||
 | 
			
		||||
Case conditions, together with [`type_of()`](type-of.md), makes it extremely easy to work with
 | 
			
		||||
values which may be of several different types (like properties in a JSON object).
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
switch value.type_of() {
 | 
			
		||||
    // if 'value' is a string...
 | 
			
		||||
    "string" if value.len() < 5 => ...,
 | 
			
		||||
    "string" => ...,
 | 
			
		||||
 | 
			
		||||
    // if 'value' is an array...
 | 
			
		||||
    "array" => ...,
 | 
			
		||||
 | 
			
		||||
    // if 'value' is an object map...
 | 
			
		||||
    "map" if value.prop == 42 => ...,
 | 
			
		||||
    "map" => ...,
 | 
			
		||||
 | 
			
		||||
    // if 'value' is a number...
 | 
			
		||||
    "i64" if value > 0 => ...,
 | 
			
		||||
    "i64" => ...,
 | 
			
		||||
 | 
			
		||||
    // anything else: probably an error...
 | 
			
		||||
    _ => ...
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Range Cases
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
Because of their popularity, [literal](../appendix/literals.md) integer [ranges](ranges.md) can also
 | 
			
		||||
be used as `switch` cases.
 | 
			
		||||
 | 
			
		||||
Numeric [ranges](ranges.md) are only searched when the `switch` value is itself a number (including
 | 
			
		||||
floating-point and decimal). They never match any other data types.
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "Must come after numeric cases"
 | 
			
		||||
 | 
			
		||||
Range cases must come _after_ all numeric cases.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = 42;
 | 
			
		||||
 | 
			
		||||
switch x {
 | 
			
		||||
    'x' => ...,             // no match: wrong data type
 | 
			
		||||
 | 
			
		||||
    1 => ...,               // <- specific numeric cases are checked first
 | 
			
		||||
    2 => ...,               // <- but these do not match
 | 
			
		||||
 | 
			
		||||
    0..50 if x > 45 => ..., // no match: condition is 'false'
 | 
			
		||||
 | 
			
		||||
    -10..20 => ...,         // no match: not in range
 | 
			
		||||
 | 
			
		||||
    0..50 => ...,           // <- MATCH!!! duplicated range cases are OK
 | 
			
		||||
 | 
			
		||||
    30..100 => ...,         // no match: even though it is within range,
 | 
			
		||||
                            // the previous case matches first
 | 
			
		||||
 | 
			
		||||
    42 => ...,              // <- syntax error: numeric cases cannot follow range cases
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Ranges can overlap"
 | 
			
		||||
 | 
			
		||||
When more then one [range](ranges.md) contain the `switch` value, the _first_ one with a fulfilled condition
 | 
			
		||||
(if any) is evaluated.
 | 
			
		||||
 | 
			
		||||
Numeric [range](ranges.md) cases are tried in the order that they appear in the original script.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Switch Expression
 | 
			
		||||
=================
 | 
			
		||||
 | 
			
		||||
Like [`if`](if.md), `switch` also works as an _expression_.
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip"
 | 
			
		||||
 | 
			
		||||
This means that a `switch` expression can appear anywhere a regular expression can,
 | 
			
		||||
e.g. as [function](functions.md) call arguments.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = switch foo { 1 => true, _ => false };
 | 
			
		||||
 | 
			
		||||
func(switch foo {
 | 
			
		||||
    "hello" => 42,
 | 
			
		||||
    "world" => 123,
 | 
			
		||||
    _ => 0
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// The above is somewhat equivalent to:
 | 
			
		||||
 | 
			
		||||
let x = if foo == 1 { true } else { false };
 | 
			
		||||
 | 
			
		||||
if foo == "hello" {
 | 
			
		||||
    func(42);
 | 
			
		||||
} else if foo == "world" {
 | 
			
		||||
    func(123);
 | 
			
		||||
} else {
 | 
			
		||||
    func(0);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Difference From `if`-`else if` Chain
 | 
			
		||||
------------------------------------
 | 
			
		||||
 | 
			
		||||
Although a `switch` expression looks _almost_ the same as an [`if`-`else if`](if.md) chain, there
 | 
			
		||||
are subtle differences between the two.
 | 
			
		||||
 | 
			
		||||
### Look-up Table vs `x == y`
 | 
			
		||||
 | 
			
		||||
A `switch` expression matches through _hashing_ via a look-up table. Therefore, matching is very
 | 
			
		||||
fast.  Walking down an [`if`-`else if`](if.md) chain is _much_ slower.
 | 
			
		||||
 | 
			
		||||
On the other hand, operators can be [overloaded](overload.md) in Rhai, meaning that it is possible
 | 
			
		||||
to override the `==` operator for integers such that `x == y` returns a different result from the
 | 
			
		||||
built-in default.
 | 
			
		||||
 | 
			
		||||
`switch` expressions do _not_ use the `==` operator for comparison; instead, they _hash_ the data
 | 
			
		||||
values and jump directly to the correct statements via a pre-compiled look-up table.  This makes
 | 
			
		||||
matching extremely efficient, but it also means that [overloading](overload.md) the `==` operator
 | 
			
		||||
will have no effect.
 | 
			
		||||
 | 
			
		||||
Therefore, in environments where it is desirable to [overload](overload.md) the `==` operator for
 | 
			
		||||
[standard types](values-and-types.md) – though it is difficult to think of valid scenarios
 | 
			
		||||
where you'd want `1 == 1` to return something other than `true` – avoid using the `switch`
 | 
			
		||||
expression.
 | 
			
		||||
 | 
			
		||||
### Efficiency
 | 
			
		||||
 | 
			
		||||
Because the `switch` expression works through a look-up table, it is very efficient even for _large_
 | 
			
		||||
number of cases; in fact, switching is an O(1) operation regardless of the size of the data and
 | 
			
		||||
number of cases to match.
 | 
			
		||||
 | 
			
		||||
A long [`if`-`else if`](if.md) chain becomes increasingly slower with each additional case because
 | 
			
		||||
essentially an O(n) _linear scan_ is performed.
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
Throw Exception on Error
 | 
			
		||||
========================
 | 
			
		||||
 | 
			
		||||
To deliberately return an error, use the `throw` keyword.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
if some_bad_condition_has_happened {
 | 
			
		||||
    throw error;    // 'throw' any value as the exception
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
throw;              // defaults to '()'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Catch a Thrown Exception
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
It is possible to _catch_ an exception, instead of having it abort the script run, via the
 | 
			
		||||
[`try` ... `catch`](try-catch.md) statement common to many C-like languages.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
fn code_that_throws() {
 | 
			
		||||
    throw 42;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
try
 | 
			
		||||
{
 | 
			
		||||
    code_that_throws();
 | 
			
		||||
}
 | 
			
		||||
catch (err)         // 'err' captures the thrown exception value
 | 
			
		||||
{
 | 
			
		||||
    print(err);     // prints 42
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
Timestamps
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
Timestamps are provided by the via the `timestamp` function.
 | 
			
		||||
 | 
			
		||||
[`type_of()`](type-of.md) a timestamp returns `"timestamp"`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Built-in Functions
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
The following methods operate on timestamps.
 | 
			
		||||
 | 
			
		||||
| Function                      | Parameter(s)                                                | Description                                                           |
 | 
			
		||||
| ----------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------- |
 | 
			
		||||
| `elapsed` method and property | _none_                                                      | returns the number of seconds since the timestamp                     |
 | 
			
		||||
| `+` operator                  | number of seconds to add                                    | returns a new timestamp with a specified number of seconds added      |
 | 
			
		||||
| `+=` operator                 | number of seconds to add                                    | adds a specified number of seconds to the timestamp                   |
 | 
			
		||||
| `-` operator                  | number of seconds to subtract                               | returns a new timestamp with a specified number of seconds subtracted |
 | 
			
		||||
| `-=` operator                 | number of seconds to subtract                               | subtracts a specified number of seconds from the timestamp            |
 | 
			
		||||
| `-` operator                  | <ol><li>later timestamp</li><li>earlier timestamp</li></ol> | returns the number of seconds between the two timestamps              |
 | 
			
		||||
 | 
			
		||||
The following time-related functions are also available.
 | 
			
		||||
 | 
			
		||||
| Function | Parameter(s)               | Description                                                 |
 | 
			
		||||
| -------- | -------------------------- | ----------------------------------------------------------- |
 | 
			
		||||
| `sleep`  | number of seconds to sleep | blocks the current thread for a specified number of seconds |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Examples
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let now = timestamp();
 | 
			
		||||
 | 
			
		||||
// Do some lengthy operation...
 | 
			
		||||
 | 
			
		||||
if now.elapsed > 30.0 {
 | 
			
		||||
    print("takes too long (over 30 seconds)!")
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
@@ -1,108 +0,0 @@
 | 
			
		||||
Catch Exceptions
 | 
			
		||||
================
 | 
			
		||||
 | 
			
		||||
When an [exception](throw.md) is thrown via a [`throw`](throw.md) statement, the script halts with
 | 
			
		||||
the exception value.
 | 
			
		||||
 | 
			
		||||
It is possible, via the `try` ... `catch` statement, to _catch_ exceptions, optionally with an
 | 
			
		||||
_error variable_.
 | 
			
		||||
 | 
			
		||||
> `try` `{` ... `}` `catch` `{` ... `}`
 | 
			
		||||
>
 | 
			
		||||
> `try` `{` ... `}` `catch` `(` _error variable_ `)` `{` ... `}`
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// Catch an exception and capturing its value
 | 
			
		||||
try
 | 
			
		||||
{
 | 
			
		||||
    throw 42;
 | 
			
		||||
}
 | 
			
		||||
catch (err)         // 'err' captures the thrown exception value
 | 
			
		||||
{
 | 
			
		||||
    print(err);     // prints 42
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Catch an exception without capturing its value
 | 
			
		||||
try
 | 
			
		||||
{
 | 
			
		||||
    print(42/0);    // deliberate divide-by-zero exception
 | 
			
		||||
}
 | 
			
		||||
catch               // no error variable - exception value is discarded
 | 
			
		||||
{
 | 
			
		||||
    print("Ouch!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exception in the 'catch' block
 | 
			
		||||
try
 | 
			
		||||
{
 | 
			
		||||
    print(42/0);    // throw divide-by-zero exception
 | 
			
		||||
}
 | 
			
		||||
catch
 | 
			
		||||
{
 | 
			
		||||
    print("You seem to be dividing by zero here...");
 | 
			
		||||
 | 
			
		||||
    throw "die";    // a 'throw' statement inside a 'catch' block
 | 
			
		||||
                    // throws a new exception
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
~~~admonish tip "Tip: Re-throw exception"
 | 
			
		||||
 | 
			
		||||
Like the `try` ... `catch` syntax in most languages, it is possible to _re-throw_ an exception
 | 
			
		||||
within the `catch` block simply by another [`throw`](throw.md) statement without a value.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
try
 | 
			
		||||
{
 | 
			
		||||
    // Call something that will throw an exception...
 | 
			
		||||
    do_something_bad_that_throws();
 | 
			
		||||
}
 | 
			
		||||
catch
 | 
			
		||||
{
 | 
			
		||||
    print("Oooh! You've done something real bad!");
 | 
			
		||||
 | 
			
		||||
    throw;          // 'throw' without a value within a 'catch' block
 | 
			
		||||
                    // re-throws the original exception
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
```admonish success "Catchable exceptions"
 | 
			
		||||
 | 
			
		||||
Many script-oriented exceptions can be caught via `try` ... `catch`.
 | 
			
		||||
 | 
			
		||||
| Error type                                                                                      |              Error value               |
 | 
			
		||||
| ----------------------------------------------------------------------------------------------- | :------------------------------------: |
 | 
			
		||||
| Runtime error thrown by a [`throw`](throw.md) statement                                         | value in [`throw`](throw.md) statement |
 | 
			
		||||
| Arithmetic error                                                                                |      [object map](object-maps.md)      |
 | 
			
		||||
| [Variable](variables.md) not found                                                              |      [object map](object-maps.md)      |
 | 
			
		||||
| [Function](functions.md) not found                                                              |      [object map](object-maps.md)      |
 | 
			
		||||
| [Module](modules/index.md) not found                                                            |      [object map](object-maps.md)      |
 | 
			
		||||
| Unbound `this`                                                                                  |      [object map](object-maps.md)      |
 | 
			
		||||
| Data type mismatch                                                                              |      [object map](object-maps.md)      |
 | 
			
		||||
| Assignment to a calculated/[constant](constants.md) value                                       |      [object map](object-maps.md)      |
 | 
			
		||||
| [Array](arrays.md)/[string](strings-chars.md)/[bit-field](bit-fields.md) indexing out-of-bounds |      [object map](object-maps.md)      |
 | 
			
		||||
| Indexing with an inappropriate data type                                                        |      [object map](object-maps.md)      |
 | 
			
		||||
| Error in property access                                                                        |      [object map](object-maps.md)      |
 | 
			
		||||
| [`for`](for.md) statement on a type that is not iterable                                        |      [object map](object-maps.md)      |
 | 
			
		||||
| Data race detected                                                                              |      [object map](object-maps.md)      |
 | 
			
		||||
| Other runtime error                                                                             |      [object map](object-maps.md)      |
 | 
			
		||||
 | 
			
		||||
The error value in the `catch` clause is an [object map](object-maps.md) containing information on
 | 
			
		||||
the particular error, including its type, line and character position (if any), and source etc.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish failure "Non-catchable exceptions"
 | 
			
		||||
 | 
			
		||||
Some system exceptions _cannot_ be caught.
 | 
			
		||||
 | 
			
		||||
| Error type                                                              | Notes                             |
 | 
			
		||||
| ----------------------------------------------------------------------- | --------------------------------- |
 | 
			
		||||
| System error – e.g. script file not found                         | system errors are not recoverable |
 | 
			
		||||
| Syntax error during parsing                                             | invalid script                    |
 | 
			
		||||
| [Custom syntax] mismatch error                                          | incompatible [`Engine`] instance  |
 | 
			
		||||
| Script evaluation metrics exceeding [limits][safety]                    | [safety] protection               |
 | 
			
		||||
| Script evaluation manually [terminated]({{rootUrl}}/safety/progress.md) | [safety] protection               |
 | 
			
		||||
```
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
`type_of()`
 | 
			
		||||
===========
 | 
			
		||||
 | 
			
		||||
The `type_of` function detects the actual type of a value.
 | 
			
		||||
 | 
			
		||||
This is useful because all [variables](variables.md) are dynamic in nature.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
// Use 'type_of()' to get the actual types of values
 | 
			
		||||
type_of('c') == "char";
 | 
			
		||||
type_of(42) == "i64";
 | 
			
		||||
 | 
			
		||||
let x = 123;
 | 
			
		||||
x.type_of() == "i64";       // method-call style is also OK
 | 
			
		||||
type_of(x) == "i64";
 | 
			
		||||
 | 
			
		||||
x = 99.999;
 | 
			
		||||
type_of(x) == "f64";
 | 
			
		||||
 | 
			
		||||
x = "hello";
 | 
			
		||||
if type_of(x) == "string" {
 | 
			
		||||
    do_something_first_with_string(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
switch type_of(x) {
 | 
			
		||||
    "string" => do_something_with_string(x),
 | 
			
		||||
    "char" => do_something_with_char(x),
 | 
			
		||||
    "i64" => do_something_with_int(x),
 | 
			
		||||
    "f64" => do_something_with_float(x),
 | 
			
		||||
    "bool" => do_something_with_bool(x),
 | 
			
		||||
    _ => throw `I cannot work with ${type_of(x)}!!!`
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish info.small "Standard types"
 | 
			
		||||
 | 
			
		||||
See [here](values-and-types.md) for the `type_of` output of standard types.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
Value Types
 | 
			
		||||
===========
 | 
			
		||||
 | 
			
		||||
The following primitive value types are supported natively.
 | 
			
		||||
 | 
			
		||||
| Category                                                                                           | [`type_of()`](type-of.md) | `to_string()`                   |
 | 
			
		||||
| -------------------------------------------------------------------------------------------------- | ------------------------- | ------------------------------- |
 | 
			
		||||
| **System integer**                                                                                 | `"i32"` or `"i64"`        | `"42"`, `"123"` etc.            |
 | 
			
		||||
| **Other integer number**                                                                           | `"i32"`, `"u64"` etc.     | `"42"`, `"123"` etc.            |
 | 
			
		||||
| **Integer numeric [range](ranges.md)**                                                             | `"range"`, `"range="`     | `"2..7"`, `"0..=15"` etc.       |
 | 
			
		||||
| **Floating-point number**                                                                          | `"f32"` or `"f64"`        | `"123.4567"` etc.               |
 | 
			
		||||
| **Fixed precision decimal number**                                                                 | `"decimal"`               | `"42"`, `"123.4567"` etc.       |
 | 
			
		||||
| **Boolean value**                                                                                  | `"bool"`                  | `"true"` or `"false"`           |
 | 
			
		||||
| **Unicode character**                                                                              | `"char"`                  | `"A"`, `"x"` etc.               |
 | 
			
		||||
| **Immutable Unicode [string](strings-chars.md)**                                                   | `"string"`                | `"hello"` etc.                  |
 | 
			
		||||
| **[`Array`](arrays.md)**                                                                           | `"array"`                 | `"[ 1, 2, 3 ]"` etc.            |
 | 
			
		||||
| **Byte array – [`BLOB`](blobs.md)**                                                          | `"blob"`                  | `"[01020304abcd]"` etc.         |
 | 
			
		||||
| **[Object map](object-maps.md)**                                                                   | `"map"`                   | `"#{ "a": 1, "b": true }"` etc. |
 | 
			
		||||
| **[Timestamp](timestamps.md)**                                                                     | `"timestamp"`             | `"<timestamp>"`                 |
 | 
			
		||||
| **[Function pointer](fn-ptr.md)**                                                                  | `"Fn"`                    | `"Fn(foo)"` etc.                |
 | 
			
		||||
| **Dynamic value** (i.e. can be anything)                                                           | _the actual type_         | _actual value_                  |
 | 
			
		||||
| **Shared value** (a reference-counted, shared dynamic value, created via [closures](fn-closure.md) | _the actual type_         | _actual value_                  |
 | 
			
		||||
| **Nothing/void/nil/null/Unit** (or whatever it is called)                                          | `"()"`                    | `""` _(empty string)_           |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "All types are distinct"
 | 
			
		||||
 | 
			
		||||
All types are treated strictly distinct by Rhai, meaning that `i32` and `i64` and `u32` are
 | 
			
		||||
completely different. They cannot even be added together.
 | 
			
		||||
 | 
			
		||||
This is very similar to Rust.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish info.small "Strings"
 | 
			
		||||
 | 
			
		||||
[Strings](strings-chars.md) in Rhai are _immutable_, meaning that they can be shared but not modified.
 | 
			
		||||
 | 
			
		||||
Any modification done to a Rhai string causes the [string](strings-chars.md) to be cloned and
 | 
			
		||||
the modifications made to the copy.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: Convert to string"
 | 
			
		||||
 | 
			
		||||
The `to_string` function converts a standard type into a [string](strings-chars.md) for display purposes.
 | 
			
		||||
 | 
			
		||||
The `to_debug` function converts a standard type into a [string](strings-chars.md) in debug format.
 | 
			
		||||
```
 | 
			
		||||
@@ -1,154 +0,0 @@
 | 
			
		||||
Variables
 | 
			
		||||
=========
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Valid Names
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
Variables in Rhai follow normal C naming rules – must contain only ASCII letters, digits and underscores `_`.
 | 
			
		||||
 | 
			
		||||
| Character set | Description              |
 | 
			
		||||
| :-----------: | ------------------------ |
 | 
			
		||||
|  `A` ... `Z`  | Upper-case ASCII letters |
 | 
			
		||||
|  `a` ... `z`  | Lower-case ASCII letters |
 | 
			
		||||
|  `0` ... `9`  | Digit characters         |
 | 
			
		||||
|      `_`      | Underscore character     |
 | 
			
		||||
 | 
			
		||||
However, a variable name must also contain at least one ASCII letter, and an ASCII
 | 
			
		||||
letter must come _before_ any digits. In other words, the first character that is not an underscore `_`
 | 
			
		||||
must be an ASCII letter and not a digit.
 | 
			
		||||
 | 
			
		||||
```admonish question.side.wide "Why this restriction?"
 | 
			
		||||
 | 
			
		||||
To reduce confusion (and subtle bugs) because, for instance, `_1` can easily be misread (or mistyped)
 | 
			
		||||
as `-1`.
 | 
			
		||||
 | 
			
		||||
Rhai is dynamic without type checking, so there is no compiler to catch these typos.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Therefore, some names, e.g. `_`, `_42foo`, `_1` etc., are not valid in Rhai.
 | 
			
		||||
 | 
			
		||||
For example: `c3po` and `_r2d2_` are valid variable names, but `3abc` and `____49steps` are not.
 | 
			
		||||
 | 
			
		||||
Variable names are case _sensitive_.
 | 
			
		||||
 | 
			
		||||
Variable names also cannot be the same as a [keyword](keywords.md) (active or reserved).
 | 
			
		||||
 | 
			
		||||
```admonish warning "Avoid names longer than 11 letters on 32-Bit"
 | 
			
		||||
 | 
			
		||||
Rhai _inlines_ a string, which avoids allocations unless it is over its internal limit
 | 
			
		||||
(23 ASCII characters on 64-bit, but only 11 ASCII characters on 32-bit).
 | 
			
		||||
 | 
			
		||||
On 64-bit systems, _most_ variable names are shorter than 23 letters, so this is unlikely to become
 | 
			
		||||
an issue.
 | 
			
		||||
 | 
			
		||||
However, on 32-bit systems, take care to limit, where possible, variable names to within 11 letters.
 | 
			
		||||
This is particularly true for local variables inside a hot loop, where they are created and
 | 
			
		||||
destroyed in rapid succession.
 | 
			
		||||
 | 
			
		||||
~~~js
 | 
			
		||||
// The following is SLOW on 32-bit
 | 
			
		||||
for my_super_loop_variable in array {
 | 
			
		||||
    print(`Super! ${my_super_loop_variable}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Suggested revision:
 | 
			
		||||
for loop_var in array {
 | 
			
		||||
    print(`Super! ${loop_var}`);
 | 
			
		||||
}
 | 
			
		||||
~~~
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Declare a Variable
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
Variables are declared using the `let` keyword.
 | 
			
		||||
 | 
			
		||||
```admonish tip.small "Tip: No initial value"
 | 
			
		||||
 | 
			
		||||
Variables do not have to be given an initial value.
 | 
			
		||||
If none is provided, it defaults to `()`.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```admonish warning.small "Variables are local"
 | 
			
		||||
 | 
			
		||||
A variable defined within a [statements block](statements.md) is _local_ to that block.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
~~~admonish tip.small "Tip: `is_def_var`"
 | 
			
		||||
 | 
			
		||||
Use `is_def_var` to detect if a variable is defined.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x;              // ok - value is '()'
 | 
			
		||||
let x = 3;          // ok
 | 
			
		||||
let _x = 42;        // ok
 | 
			
		||||
let x_ = 42;        // also ok
 | 
			
		||||
let _x_ = 42;       // still ok
 | 
			
		||||
 | 
			
		||||
let _ = 123;        // <- syntax error: illegal variable name
 | 
			
		||||
let _9 = 9;         // <- syntax error: illegal variable name
 | 
			
		||||
 | 
			
		||||
let x = 42;         // variable is 'x', lower case
 | 
			
		||||
let X = 123;        // variable is 'X', upper case
 | 
			
		||||
 | 
			
		||||
print(x);           // prints 42
 | 
			
		||||
print(X);           // prints 123
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    let x = 999;    // local variable 'x' shadows the 'x' in parent block
 | 
			
		||||
 | 
			
		||||
    print(x);       // prints 999
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print(x);           // prints 42 - the parent block's 'x' is not changed
 | 
			
		||||
 | 
			
		||||
let x = 0;          // new variable 'x' shadows the old 'x'
 | 
			
		||||
 | 
			
		||||
print(x);           // prints 0
 | 
			
		||||
 | 
			
		||||
is_def_var("x") == true;
 | 
			
		||||
 | 
			
		||||
is_def_var("_x") == true;
 | 
			
		||||
 | 
			
		||||
is_def_var("y") == false;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Shadowing
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
New variables automatically _shadow_ existing ones of the same name.  There is no error.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 42;
 | 
			
		||||
let y = 123;
 | 
			
		||||
 | 
			
		||||
print(x);           // prints 42
 | 
			
		||||
 | 
			
		||||
let x = 88;         // <- 'x' is shadowed here
 | 
			
		||||
 | 
			
		||||
// At this point, it is no longer possible to access the
 | 
			
		||||
// original 'x' on the first line...
 | 
			
		||||
 | 
			
		||||
print(x);           // prints 88
 | 
			
		||||
 | 
			
		||||
let x = 0;          // <- 'x' is shadowed again
 | 
			
		||||
 | 
			
		||||
// At this point, it is no longer possible to access both
 | 
			
		||||
// previously-defined 'x'...
 | 
			
		||||
 | 
			
		||||
print(x);           // prints 0
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    let x = 999;    // <- 'x' is shadowed in a block
 | 
			
		||||
    
 | 
			
		||||
    print(x);       // prints 999
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print(x);           // prints 0 - shadowing within the block goes away
 | 
			
		||||
 | 
			
		||||
print(y);           // prints 123 - 'y' is not shadowed
 | 
			
		||||
```
 | 
			
		||||
@@ -1,50 +0,0 @@
 | 
			
		||||
While Loop
 | 
			
		||||
==========
 | 
			
		||||
 | 
			
		||||
`while` loops follow C syntax.
 | 
			
		||||
 | 
			
		||||
`continue` can be used to skip to the next iteration, by-passing all following statements;
 | 
			
		||||
`break` can be used to break out of the loop unconditionally.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
let x = 10;
 | 
			
		||||
 | 
			
		||||
while x > 0 {
 | 
			
		||||
    x -= 1;
 | 
			
		||||
    if x < 6 { continue; }  // skip to the next iteration
 | 
			
		||||
    print(x);
 | 
			
		||||
    if x == 5 { break; }    // break out of while loop
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
While Expression
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
`while` statements can also be used as _expressions_.
 | 
			
		||||
 | 
			
		||||
The `break` statement takes an optional expression that provides the return value.
 | 
			
		||||
 | 
			
		||||
The default return value of a `while` expression is `()`.
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
let x = 0;
 | 
			
		||||
 | 
			
		||||
// 'while' can be used just like an expression
 | 
			
		||||
let result = while x < 100 {
 | 
			
		||||
    if is_magic_number(x) {
 | 
			
		||||
        // if the 'while' loop breaks here, return a specific value
 | 
			
		||||
        break get_magic_result(x);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    x += 1;
 | 
			
		||||
 | 
			
		||||
    // ... if the 'while' loop exits here, the return value is ()
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if result == () {
 | 
			
		||||
    print("Magic number not found!");
 | 
			
		||||
} else {
 | 
			
		||||
    print(`Magic result = ${result}!`);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
		Reference in New Issue
	
	Block a user