rhai/doc/src/rust/custom.md
2020-06-20 12:06:17 +08:00

149 lines
4.1 KiB
Markdown

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"
```