Add Rhai book.
This commit is contained in:
148
doc/src/rust/custom.md
Normal file
148
doc/src/rust/custom.md
Normal file
@@ -0,0 +1,148 @@
|
||||
Register a Custom Type and its Methods
|
||||
=====================================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Rhai works seamlessly with _any_ complex Rust type. The type can be registered with the `Engine`, as below.
|
||||
|
||||
Support for custom types can be turned off via the [`no_object`] feature.
|
||||
|
||||
```rust
|
||||
use rhai::{Engine, EvalAltResult};
|
||||
use rhai::RegisterFn;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
field: i64
|
||||
}
|
||||
|
||||
impl TestStruct {
|
||||
fn update(&mut self) {
|
||||
self.field += 41;
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
TestStruct { field: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>>
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
engine.register_type::<TestStruct>();
|
||||
|
||||
engine.register_fn("update", TestStruct::update);
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
|
||||
|
||||
println!("result: {}", result.field); // prints 42
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
Register a Custom Type
|
||||
---------------------
|
||||
|
||||
A custom type must implement `Clone` as this allows the [`Engine`] to pass by value.
|
||||
|
||||
Notice that the custom type needs to be _registered_ using `Engine::register_type`.
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
field: i64
|
||||
}
|
||||
|
||||
impl TestStruct {
|
||||
fn update(&mut self) { // methods take &mut as first parameter
|
||||
self.field += 41;
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
TestStruct { field: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
let engine = Engine::new();
|
||||
|
||||
engine.register_type::<TestStruct>();
|
||||
```
|
||||
|
||||
Methods on Custom Type
|
||||
---------------------
|
||||
|
||||
To use native custom types, methods and functions in Rhai scripts, simply register them
|
||||
using one of the `Engine::register_XXX` API.
|
||||
|
||||
Below, the `update` and `new` methods are registered using `Engine::register_fn`.
|
||||
|
||||
```rust
|
||||
engine.register_fn("update", TestStruct::update); // registers 'update(&mut TestStruct)'
|
||||
engine.register_fn("new_ts", TestStruct::new); // registers 'new()'
|
||||
```
|
||||
|
||||
***Note**: Rhai follows the convention that methods of custom types take a `&mut` first parameter
|
||||
so that invoking methods can update the types. All other parameters in Rhai are passed by value (i.e. clones).*
|
||||
|
||||
Use the Custom Type in Scripts
|
||||
-----------------------------
|
||||
|
||||
The custom type is then ready for use in scripts. Scripts can see the functions and methods registered earlier.
|
||||
Get the evaluation result back out just as before, this time casting to the custom type:
|
||||
|
||||
```rust
|
||||
let result = engine.eval::<TestStruct>("let x = new_ts(); x.update(); x")?;
|
||||
|
||||
println!("result: {}", result.field); // prints 42
|
||||
```
|
||||
|
||||
Method-Call Style vs. Function-Call Style
|
||||
----------------------------------------
|
||||
|
||||
In fact, any function with a first argument that is a `&mut` reference can be used as method calls because
|
||||
internally they are the same thing: methods on a type is implemented as a functions taking a `&mut` first argument.
|
||||
|
||||
```rust
|
||||
fn foo(ts: &mut TestStruct) -> i64 {
|
||||
ts.field
|
||||
}
|
||||
|
||||
engine.register_fn("foo", foo); // register ad hoc function with correct signature
|
||||
|
||||
let result = engine.eval::<i64>(
|
||||
"let x = new_ts(); x.foo()" // 'foo' can be called like a method on 'x'
|
||||
)?;
|
||||
|
||||
println!("result: {}", result); // prints 1
|
||||
```
|
||||
|
||||
Under [`no_object`], however, the _method_ style of function calls (i.e. calling a function as an object-method)
|
||||
is no longer supported.
|
||||
|
||||
```rust
|
||||
// Below is a syntax error under 'no_object' because 'clear' cannot be called in method style.
|
||||
let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?;
|
||||
```
|
||||
|
||||
[`type_of()`]
|
||||
-------------
|
||||
|
||||
[`type_of()`] works fine with custom types and returns the name of the type.
|
||||
|
||||
If `Engine::register_type_with_name` is used to register the custom type
|
||||
with a special "pretty-print" name, [`type_of()`] will return that name instead.
|
||||
|
||||
```rust
|
||||
engine.register_type::<TestStruct>();
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
let x = new_ts();
|
||||
print(x.type_of()); // prints "path::to::module::TestStruct"
|
||||
|
||||
engine.register_type_with_name::<TestStruct>("Hello");
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
let x = new_ts();
|
||||
print(x.type_of()); // prints "Hello"
|
||||
```
|
10
doc/src/rust/disable-custom.md
Normal file
10
doc/src/rust/disable-custom.md
Normal file
@@ -0,0 +1,10 @@
|
||||
Disable Custom Types
|
||||
====================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
The custom types API `register_type`, `register_type_with_name`, `register_get`, `register_set`, `register_get_set`,
|
||||
`register_indexer_get`, `register_indexer_set` and `register_indexer_get_set` are not available under [`no_object`].
|
||||
|
||||
The indexers API `register_indexer_get`, `register_indexer_set` and `register_indexer_get_set` are also
|
||||
not available under [`no_index`].
|
41
doc/src/rust/fallible.md
Normal file
41
doc/src/rust/fallible.md
Normal file
@@ -0,0 +1,41 @@
|
||||
Register a Fallible Rust Function
|
||||
================================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
If a function is _fallible_ (i.e. it returns a `Result<_, Error>`), it can be registered with `register_result_fn`
|
||||
(using the `RegisterResultFn` trait).
|
||||
|
||||
The function must return `Result<Dynamic, Box<EvalAltResult>>`.
|
||||
|
||||
`Box<EvalAltResult>` implements `From<&str>` and `From<String>` etc.
|
||||
and the error text gets converted into `Box<EvalAltResult::ErrorRuntime>`.
|
||||
|
||||
The error values are `Box`-ed in order to reduce memory footprint of the error path, which should be hit rarely.
|
||||
|
||||
```rust
|
||||
use rhai::{Engine, EvalAltResult, Position};
|
||||
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
|
||||
|
||||
// Function that may fail - the result type must be 'Dynamic'
|
||||
fn safe_divide(x: i64, y: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
if y == 0 {
|
||||
// Return an error if y is zero
|
||||
Err("Division by zero!".into()) // short-cut to create Box<EvalAltResult::ErrorRuntime>
|
||||
} else {
|
||||
Ok((x / y).into()) // convert result into 'Dynamic'
|
||||
}
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
// Fallible functions that return Result values must use register_result_fn()
|
||||
engine.register_result_fn("divide", safe_divide);
|
||||
|
||||
if let Err(error) = engine.eval::<i64>("divide(40, 0)") {
|
||||
println!("Error: {:?}", *error); // prints ErrorRuntime("Division by zero detected!", (1, 1)")
|
||||
}
|
||||
}
|
||||
```
|
73
doc/src/rust/functions.md
Normal file
73
doc/src/rust/functions.md
Normal file
@@ -0,0 +1,73 @@
|
||||
Register a Rust Function
|
||||
========================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Rhai's scripting engine is very lightweight. It gets most of its abilities from functions.
|
||||
|
||||
To call these functions, they need to be _registered_ with the [`Engine`] using `Engine::register_fn`
|
||||
(in the `RegisterFn` trait) and `Engine::register_result_fn` (in the `RegisterResultFn` trait,
|
||||
see [fallible functions](/rust/fallible.md)).
|
||||
|
||||
```rust
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString};
|
||||
use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn'
|
||||
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
|
||||
|
||||
// Normal function that returns a standard type
|
||||
// Remember to use 'ImmutableString' and not 'String'
|
||||
fn add_len(x: i64, s: ImmutableString) -> i64 {
|
||||
x + s.len()
|
||||
}
|
||||
// Alternatively, '&str' maps directly to 'ImmutableString'
|
||||
fn add_len_str(x: i64, s: &str) -> i64 {
|
||||
x + s.len()
|
||||
}
|
||||
|
||||
// Function that returns a 'Dynamic' value - must return a 'Result'
|
||||
fn get_any_value() -> Result<Dynamic, Box<EvalAltResult>> {
|
||||
Ok((42_i64).into()) // standard types can use 'into()'
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>>
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
engine.register_fn("add", add_len);
|
||||
engine.register_fn("add_str", add_len_str);
|
||||
|
||||
let result = engine.eval::<i64>(r#"add(40, "xx")"#)?;
|
||||
|
||||
println!("Answer: {}", result); // prints 42
|
||||
|
||||
let result = engine.eval::<i64>(r#"add_str(40, "xx")"#)?;
|
||||
|
||||
println!("Answer: {}", result); // prints 42
|
||||
|
||||
// Functions that return Dynamic values must use register_result_fn()
|
||||
engine.register_result_fn("get_any_value", get_any_value);
|
||||
|
||||
let result = engine.eval::<i64>("get_any_value()")?;
|
||||
|
||||
println!("Answer: {}", result); // prints 42
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
To create a [`Dynamic`] value, use the `Dynamic::from` method.
|
||||
[Standard types] in Rhai can also use `into()`.
|
||||
|
||||
```rust
|
||||
use rhai::Dynamic;
|
||||
|
||||
let x = (42_i64).into(); // 'into()' works for standard types
|
||||
|
||||
let y = Dynamic::from("hello!".to_string()); // remember &str is not supported by Rhai
|
||||
```
|
||||
|
||||
Functions registered with the [`Engine`] can be _overloaded_ as long as the _signature_ is unique,
|
||||
i.e. different functions can have the same name as long as their parameters are of different types
|
||||
and/or different number.
|
||||
|
||||
New definitions _overwrite_ previous definitions of the same name and same number/types of parameters.
|
32
doc/src/rust/generic.md
Normal file
32
doc/src/rust/generic.md
Normal file
@@ -0,0 +1,32 @@
|
||||
Register a Generic Rust Function
|
||||
===============================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Rust generic functions can be used in Rhai, but separate instances for each concrete type must be registered separately.
|
||||
|
||||
This essentially _overloads_ the function with different parameter types as Rhai does not natively support generics
|
||||
but Rhai does support _function overloading_.
|
||||
|
||||
```rust
|
||||
use std::fmt::Display;
|
||||
|
||||
use rhai::{Engine, RegisterFn};
|
||||
|
||||
fn show_it<T: Display>(x: &mut T) {
|
||||
println!("put up a good show: {}!", x)
|
||||
}
|
||||
|
||||
fn main()
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
engine.register_fn("print", show_it::<i64>);
|
||||
engine.register_fn("print", show_it::<bool>);
|
||||
engine.register_fn("print", show_it::<ImmutableString>);
|
||||
}
|
||||
```
|
||||
|
||||
The above example shows how to register multiple functions
|
||||
(or, in this case, multiple overloaded versions of the same function)
|
||||
under the same name.
|
42
doc/src/rust/getters-setters.md
Normal file
42
doc/src/rust/getters-setters.md
Normal file
@@ -0,0 +1,42 @@
|
||||
Custom Type Getters and Setters
|
||||
==============================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
A custom type can also expose members by registering `get` and/or `set` functions.
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
field: String
|
||||
}
|
||||
|
||||
impl TestStruct {
|
||||
// Returning a 'String' is OK - Rhai converts it into 'ImmutableString'
|
||||
fn get_field(&mut self) -> String {
|
||||
self.field.clone()
|
||||
}
|
||||
|
||||
// Remember Rhai uses 'ImmutableString' or '&str' instead of 'String'
|
||||
fn set_field(&mut self, new_val: ImmutableString) {
|
||||
// Get a 'String' from an 'ImmutableString'
|
||||
self.field = (*new_val).clone();
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
TestStruct { field: "hello" }
|
||||
}
|
||||
}
|
||||
|
||||
let engine = Engine::new();
|
||||
|
||||
engine.register_type::<TestStruct>();
|
||||
|
||||
engine.register_get_set("xyz", TestStruct::get_field, TestStruct::set_field);
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
// Return result can be 'String' - Rhai will automatically convert it from 'ImmutableString'
|
||||
let result = engine.eval::<String>(r#"let a = new_ts(); a.xyz = "42"; a.xyz"#)?;
|
||||
|
||||
println!("Answer: {}", result); // prints 42
|
||||
```
|
47
doc/src/rust/indexers.md
Normal file
47
doc/src/rust/indexers.md
Normal file
@@ -0,0 +1,47 @@
|
||||
Custom Type Indexers
|
||||
===================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
A custom type can also expose an _indexer_ by registering an indexer function.
|
||||
|
||||
A custom type with an indexer function defined can use the bracket '`[]`' notation to get a property value.
|
||||
|
||||
Indexers are disabled when the [`no_index`] feature is used.
|
||||
|
||||
```rust
|
||||
#[derive(Clone)]
|
||||
struct TestStruct {
|
||||
fields: Vec<i64>
|
||||
}
|
||||
|
||||
impl TestStruct {
|
||||
fn get_field(&mut self, index: i64) -> i64 {
|
||||
self.fields[index as usize]
|
||||
}
|
||||
fn set_field(&mut self, index: i64, value: i64) {
|
||||
self.fields[index as usize] = value
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
TestStruct { fields: vec![1, 2, 3, 4, 5] }
|
||||
}
|
||||
}
|
||||
|
||||
let engine = Engine::new();
|
||||
|
||||
engine.register_type::<TestStruct>();
|
||||
|
||||
engine.register_fn("new_ts", TestStruct::new);
|
||||
|
||||
// Shorthand: engine.register_indexer_get_set(TestStruct::get_field, TestStruct::set_field);
|
||||
engine.register_indexer_get(TestStruct::get_field);
|
||||
engine.register_indexer_set(TestStruct::set_field);
|
||||
|
||||
let result = engine.eval::<i64>("let a = new_ts(); a[2] = 42; a[2]")?;
|
||||
|
||||
println!("Answer: {}", result); // prints 42
|
||||
```
|
||||
|
||||
For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for
|
||||
[arrays] and [object maps].
|
57
doc/src/rust/operators.md
Normal file
57
doc/src/rust/operators.md
Normal file
@@ -0,0 +1,57 @@
|
||||
Operator Overloading
|
||||
===================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
In Rhai, a lot of functionalities are actually implemented as functions, including basic operations
|
||||
such as arithmetic calculations.
|
||||
|
||||
For example, in the expression "`a + b`", the `+` operator is _not_ built in, but calls a function named "`+`" instead!
|
||||
|
||||
```rust
|
||||
let x = a + b;
|
||||
let x = +(a, b); // <- the above is equivalent to this function call
|
||||
```
|
||||
|
||||
Similarly, comparison operators including `==`, `!=` etc. are all implemented as functions,
|
||||
with the stark exception of `&&` and `||`. Because they [_short-circuit_](/language/logic.md#boolean-operators),
|
||||
`&&` and `||` are handled specially and _not_ via a function; as a result, overriding them has no effect at all.
|
||||
|
||||
Operator functions cannot be defined as a script function (because operators syntax are not valid function names).
|
||||
|
||||
However, operator functions _can_ be registered to the [`Engine`] via the methods
|
||||
`Engine::register_fn`, `Engine::register_result_fn` etc.
|
||||
|
||||
When a custom operator function is registered with the same name as an operator, it _overrides_ the built-in version.
|
||||
|
||||
```rust
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn};
|
||||
|
||||
let mut engine = Engine::new();
|
||||
|
||||
fn strange_add(a: i64, b: i64) -> i64 { (a + b) * 42 }
|
||||
|
||||
engine.register_fn("+", strange_add); // overload '+' operator for two integers!
|
||||
|
||||
let result: i64 = engine.eval("1 + 0"); // the overloading version is used
|
||||
|
||||
println!("result: {}", result); // prints 42
|
||||
|
||||
let result: f64 = engine.eval("1.0 + 0.0"); // '+' operator for two floats not overloaded
|
||||
|
||||
println!("result: {}", result); // prints 1.0
|
||||
|
||||
fn mixed_add(a: i64, b: f64) -> f64 { (a as f64) + b }
|
||||
|
||||
engine.register_fn("+", mixed_add); // register '+' operator for an integer and a float
|
||||
|
||||
let result: i64 = engine.eval("1 + 1.0"); // prints 2.0 (normally an error)
|
||||
```
|
||||
|
||||
Normally, use operator overloading for [custom types] only.
|
||||
|
||||
Be very careful when overriding built-in operators because script authors expect standard operators to behave in a
|
||||
consistent and predictable manner, and will be annoyed if a calculation for '`+`' turns into a subtraction, for example.
|
||||
|
||||
Operator overloading also impacts script optimization when using [`OptimizationLevel::Full`].
|
||||
See the [script-optimization] for more details.
|
17
doc/src/rust/options.md
Normal file
17
doc/src/rust/options.md
Normal file
@@ -0,0 +1,17 @@
|
||||
Engine Configuration Options
|
||||
===========================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
A number of other configuration options are available from the `Engine` to fine-tune behavior and safeguards.
|
||||
|
||||
| Method | Not available under | Description |
|
||||
| ------------------------ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `set_optimization_level` | [`no_optimize`] | Set the amount of script _optimizations_ performed. See [script optimization]. |
|
||||
| `set_max_expr_depths` | [`unchecked`] | Set the maximum nesting levels of an expression/statement. See [maximum statement depth]. |
|
||||
| `set_max_call_levels` | [`unchecked`] | Set the maximum number of function call levels (default 50) to avoid infinite recursion. See [maximum call stack depth]. |
|
||||
| `set_max_operations` | [`unchecked`] | Set the maximum number of _operations_ that a script is allowed to consume. See [maximum number of operations]. |
|
||||
| `set_max_modules` | [`unchecked`] | Set the maximum number of [modules] that a script is allowed to load. See [maximum number of modules]. |
|
||||
| `set_max_string_size` | [`unchecked`] | Set the maximum length (in UTF-8 bytes) for [strings]. See [maximum length of strings]. |
|
||||
| `set_max_array_size` | [`unchecked`], [`no_index`] | Set the maximum size for [arrays]. See [maximum size of arrays]. |
|
||||
| `set_max_map_size` | [`unchecked`], [`no_object`] | Set the maximum number of properties for [object maps]. See [maximum size of object maps]. |
|
19
doc/src/rust/override.md
Normal file
19
doc/src/rust/override.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Override a Built-in Function
|
||||
===========================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Any similarly-named function defined in a script overrides any built-in or registered
|
||||
native Rust function of the same name and number of parameters.
|
||||
|
||||
```rust
|
||||
// Override the built-in function 'to_int'
|
||||
fn to_int(num) {
|
||||
print("Ha! Gotcha! " + num);
|
||||
}
|
||||
|
||||
print(to_int(123)); // what happens?
|
||||
```
|
||||
|
||||
A registered native Rust function, in turn, overrides any built-in function of the
|
||||
same name, number and types of parameters.
|
52
doc/src/rust/packages.md
Normal file
52
doc/src/rust/packages.md
Normal file
@@ -0,0 +1,52 @@
|
||||
Packages
|
||||
========
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Standard built-in Rhai features are provided in various _packages_ that can be loaded via a call to `Engine::load_package`.
|
||||
|
||||
Packages reside under `rhai::packages::*` and the trait `rhai::packages::Package` must be loaded in order for
|
||||
packages to be used.
|
||||
|
||||
```rust
|
||||
use rhai::Engine;
|
||||
use rhai::packages::Package // load the 'Package' trait to use packages
|
||||
use rhai::packages::CorePackage; // the 'core' package contains basic functionalities (e.g. arithmetic)
|
||||
|
||||
let mut engine = Engine::new_raw(); // create a 'raw' Engine
|
||||
let package = CorePackage::new(); // create a package - can be shared among multiple `Engine` instances
|
||||
|
||||
engine.load_package(package.get()); // load the package manually. 'get' returns a reference to the shared package
|
||||
```
|
||||
|
||||
The follow packages are available:
|
||||
|
||||
| Package | Description | In `Core` | In `Standard` |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------ | :-------: | :-----------: |
|
||||
| `ArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) for numeric types that are not built in (e.g. `u16`) | Yes | Yes |
|
||||
| `BasicIteratorPackage` | Numeric ranges (e.g. `range(1, 10)`) | Yes | Yes |
|
||||
| `LogicPackage` | Logical and comparison operators (e.g. `==`, `>`) for numeric types that are not built in (e.g. `u16`) | Yes | Yes |
|
||||
| `BasicStringPackage` | Basic string functions (e.g. `print`, `debug`, `len`) that are not built in | Yes | Yes |
|
||||
| `BasicTimePackage` | Basic time functions (e.g. [timestamps]) | Yes | Yes |
|
||||
| `MoreStringPackage` | Additional string functions, including converting common types to string | No | Yes |
|
||||
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
|
||||
| `BasicArrayPackage` | Basic [array] functions (not available under `no_index`) | No | Yes |
|
||||
| `BasicMapPackage` | Basic [object map] functions (not available under `no_object`) | No | Yes |
|
||||
| `EvalPackage` | Disable [`eval`] | No | No |
|
||||
| `CorePackage` | Basic essentials | Yes | Yes |
|
||||
| `StandardPackage` | Standard library (default for `Engine::new`) | No | Yes |
|
||||
|
||||
Packages typically contain Rust functions that are callable within a Rhai script.
|
||||
All functions registered in a package is loaded under the _global namespace_ (i.e. they're available without module qualifiers).
|
||||
|
||||
Once a package is created (e.g. via `new`), it can be _shared_ (via `get`) among multiple instances of [`Engine`],
|
||||
even across threads (under [`sync`]). Therefore, a package only has to be created _once_.
|
||||
|
||||
Packages are actually implemented as [modules], so they share a lot of behavior and characteristics.
|
||||
The main difference is that a package loads under the _global_ namespace, while a module loads under its own
|
||||
namespace alias specified in an [`import`] statement (see also [modules]).
|
||||
|
||||
A package is _static_ (i.e. pre-loaded into an [`Engine`]), while a module is _dynamic_ (i.e. loaded with
|
||||
the `import` statement).
|
||||
|
||||
Custom packages can also be created. See the macro [`def_package!`](https://docs.rs/rhai/0.13.0/rhai/macro.def_package.html).
|
16
doc/src/rust/print-custom.md
Normal file
16
doc/src/rust/print-custom.md
Normal file
@@ -0,0 +1,16 @@
|
||||
Printing for Custom Types
|
||||
========================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
To use custom types for [`print`] and [`debug`], or convert its value into a [string], it is necessary that the following
|
||||
functions be registered (assuming the custom type is `T : Display + Debug`):
|
||||
|
||||
| Function | Signature | Typical implementation | Usage |
|
||||
| ----------- | ------------------------------------------------ | ------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| `to_string` | `|s: &mut T| -> ImmutableString` | `s.to_string().into()` | Converts the custom type into a [string] |
|
||||
| `print` | `|s: &mut T| -> ImmutableString` | `s.to_string().into()` | Converts the custom type into a [string] for the [`print`](#print-and-debug) statement |
|
||||
| `debug` | `|s: &mut T| -> ImmutableString` | `format!("{:?}", s).into()` | Converts the custom type into a [string] for the [`debug`](#print-and-debug) statement |
|
||||
| `+` | `|s1: ImmutableString, s: T| -> ImmutableString` | `s1 + s` | Append the custom type to another [string], for `print("Answer: " + type);` usage |
|
||||
| `+` | `|s: T, s2: ImmutableString| -> ImmutableString` | `s.to_string().push_str(&s2).into();` | Append another [string] to the custom type, for `print(type + " is the answer");` usage |
|
||||
| `+=` | `|s1: &mut ImmutableString, s: T|` | `s1 += s.to_string()` | Append the custom type to an existing [string], for `s += type;` usage |
|
59
doc/src/rust/scope.md
Normal file
59
doc/src/rust/scope.md
Normal file
@@ -0,0 +1,59 @@
|
||||
`Scope` - Initializing and Maintaining State
|
||||
===========================================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
By default, Rhai treats each [`Engine`] invocation as a fresh one, persisting only the functions that have been defined
|
||||
but no global state. This gives each evaluation a clean starting slate.
|
||||
|
||||
In order to continue using the same global state from one invocation to the next,
|
||||
such a state must be manually created and passed in.
|
||||
|
||||
All `Scope` variables are [`Dynamic`], meaning they can store values of any type.
|
||||
|
||||
Under [`sync`], however, only types that are `Send + Sync` are supported, and the entire `Scope` itself
|
||||
will also be `Send + Sync`. This is extremely useful in multi-threaded applications.
|
||||
|
||||
In this example, a global state object (a `Scope`) is created with a few initialized variables,
|
||||
then the same state is threaded through multiple invocations:
|
||||
|
||||
```rust
|
||||
use rhai::{Engine, Scope, EvalAltResult};
|
||||
|
||||
fn main() -> Result<(), Box<EvalAltResult>>
|
||||
{
|
||||
let engine = Engine::new();
|
||||
|
||||
// First create the state
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Then push (i.e. add) some initialized variables into the state.
|
||||
// Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64.
|
||||
// Better stick to them or it gets hard working with the script.
|
||||
scope.push("y", 42_i64);
|
||||
scope.push("z", 999_i64);
|
||||
|
||||
// 'set_value' adds a variable when one doesn't exist
|
||||
scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str'
|
||||
|
||||
// First invocation
|
||||
engine.eval_with_scope::<()>(&mut scope, r"
|
||||
let x = 4 + 5 - y + z + s.len;
|
||||
y = 1;
|
||||
")?;
|
||||
|
||||
// Second invocation using the same state
|
||||
let result = engine.eval_with_scope::<i64>(&mut scope, "x")?;
|
||||
|
||||
println!("result: {}", result); // prints 979
|
||||
|
||||
// Variable y is changed in the script - read it with 'get_value'
|
||||
assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 1);
|
||||
|
||||
// We can modify scope variables directly with 'set_value'
|
||||
scope.set_value("y", 42_i64);
|
||||
assert_eq!(scope.get_value::<i64>("y").expect("variable y should exist"), 42);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
21
doc/src/rust/strings.md
Normal file
21
doc/src/rust/strings.md
Normal file
@@ -0,0 +1,21 @@
|
||||
`String` Parameters in Rust Functions
|
||||
====================================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Rust functions accepting parameters of `String` should use `&str` instead because it maps directly to [`ImmutableString`]
|
||||
which is the type that Rhai uses to represent [strings] internally.
|
||||
|
||||
```rust
|
||||
fn get_len1(s: String) -> i64 { s.len() as i64 } // <- Rhai will not find this function
|
||||
fn get_len2(s: &str) -> i64 { s.len() as i64 } // <- Rhai finds this function fine
|
||||
fn get_len3(s: ImmutableString) -> i64 { s.len() as i64 } // <- the above is equivalent to this
|
||||
|
||||
engine.register_fn("len1", get_len1);
|
||||
engine.register_fn("len2", get_len2);
|
||||
engine.register_fn("len3", get_len3);
|
||||
|
||||
let len = engine.eval::<i64>("x.len1()")?; // error: function 'len1 (&str | ImmutableString)' not found
|
||||
let len = engine.eval::<i64>("x.len2()")?; // works fine
|
||||
let len = engine.eval::<i64>("x.len3()")?; // works fine
|
||||
```
|
13
doc/src/rust/traits.md
Normal file
13
doc/src/rust/traits.md
Normal file
@@ -0,0 +1,13 @@
|
||||
Traits
|
||||
======
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
A number of traits, under the `rhai::` module namespace, provide additional functionalities.
|
||||
|
||||
| Trait | Description | Methods |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------- | --------------------------------------- |
|
||||
| `RegisterFn` | Trait for registering functions | `register_fn` |
|
||||
| `RegisterResultFn` | Trait for registering fallible functions returning `Result<Dynamic, Box<EvalAltResult>>` | `register_result_fn` |
|
||||
| `Func` | Trait for creating anonymous functions from script | `create_from_ast`, `create_from_script` |
|
||||
| `ModuleResolver` | Trait implemented by module resolution services | `resolve` |
|
Reference in New Issue
Block a user