Revise docs.

This commit is contained in:
Stephen Chung 2020-07-28 10:25:57 +08:00
parent f05cd1fdf3
commit b70fd35f4a
9 changed files with 91 additions and 51 deletions

View File

@ -27,12 +27,11 @@ Standard features
* Easily [call a script-defined function](https://schungx.github.io/rhai/engine/call-fn.html) from Rust.
* Fairly low compile-time overhead.
* Fairly efficient evaluation (1 million iterations in 0.3 sec on a single core, 2.3 GHz Linux VM).
* Relatively little `unsafe` code (yes there are some for performance reasons, and most `unsafe` code is limited to
one single source file, all with names starting with `"unsafe_"`).
* Relatively little `unsafe` code (yes there are some for performance reasons).
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
* [Function overloading](https://schungx.github.io/rhai/language/overload.html).
* [Operator overloading](https://schungx.github.io/rhai/rust/operators.html).
* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html).
* Dynamic dispatch via [function pointers](https://schungx.github.io/rhai/language/fn-ptr.html) with additional support for [currying](https://schungx.github.io/rhai/language/fn-curry.html).
* Some support for [object-oriented programming (OOP)](https://schungx.github.io/rhai/language/oop.html).
* Organize code base with dynamically-loadable [modules](https://schungx.github.io/rhai/language/modules.html).
* Serialization/deserialization support via [serde](https://crates.io/crates/serde) (requires the `serde` feature).

View File

@ -35,23 +35,20 @@ Dynamic
* Organize code base with dynamically-loadable [modules].
* Dynamic dispatch via [function pointers].
* Dynamic dispatch via [function pointers] with additional support for [currying].
* Some support for [object-oriented programming (OOP)][OOP].
* Serialization/deserialization support via [`serde`].
Safe
----
* Relatively little `unsafe` code (yes there are some for performance reasons, and most `unsafe` code is limited to
one single source file, all with names starting with `"unsafe_"`).
* Relatively little `unsafe` code (yes there are some for performance reasons).
* Sand-boxed - the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless explicitly permitted (e.g. via a `RefCell`).
Rugged
------
* Sand-boxed - the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless explicitly permitted (e.g. via a `RefCell`).
* Protected against malicious attacks (such as [stack-overflow][maximum call stack depth], [over-sized data][maximum length of strings], and [runaway scripts][maximum number of operations] etc.) that may come from untrusted third-party user-land scripts.
* Track script evaluation [progress] and manually terminate a script run.
@ -61,6 +58,8 @@ Flexible
* Re-entrant scripting [`Engine`] can be made `Send + Sync` (via the [`sync`] feature).
* Serialization/deserialization support via [`serde`](https://crates.io/crates/serde).
* Support for [minimal builds] by excluding unneeded language [features].
* Supports [most build targets](targets.md) including `no-std` and [WASM].

View File

@ -69,17 +69,22 @@ The only practical way to ensure that a function is a correct one is to use [mod
i.e. define the function in a separate module and then [`import`] it:
```rust
message.rhai:
----------------
| message.rhai |
----------------
fn message() { "Hello!" }
fn message() { "Hello!" }
script.rhai:
fn say_hello() {
---------------
| script.rhai |
---------------
fn say_hello() {
import "message" as msg;
print(msg::message());
}
say_hello();
}
say_hello();
```
@ -94,18 +99,23 @@ defined _within the script_. When called later, those functions will be searche
current global namespace and may not be found.
```rust
greeting.rhai:
-----------------
| greeting.rhai |
-----------------
fn message() { "Hello!" };
fn message() { "Hello!" };
fn say_hello() { print(message()); }
fn say_hello() { print(message()); }
say_hello(); // 'message' is looked up in the global namespace
say_hello(); // 'message' is looked up in the global namespace
script.rhai:
import "greeting" as g;
g::say_hello(); // <- error: function not found - 'message'
---------------
| script.rhai |
---------------
import "greeting" as g;
g::say_hello(); // <- error: function not found - 'message'
```
In the example above, although the module `greeting.rhai` loads fine (`"Hello!"` is printed),
@ -118,24 +128,29 @@ as possible and avoid cross-calling them from each other. A [function pointer]
to call another function within a module-defined function:
```rust
greeting.rhai:
-----------------
| greeting.rhai |
-----------------
fn message() { "Hello!" };
fn message() { "Hello!" };
fn say_hello(msg_func) { // 'msg_func' is a function pointer
fn say_hello(msg_func) { // 'msg_func' is a function pointer
print(msg_func.call()); // call via the function pointer
}
}
say_hello(); // 'message' is looked up in the global namespace
say_hello(); // 'message' is looked up in the global namespace
script.rhai:
import "greeting" as g;
---------------
| script.rhai |
---------------
fn my_msg() {
import "greeting" as g;
fn my_msg() {
import "greeting" as g; // <- must import again here...
g::message() // <- ... otherwise will not find module 'g'
}
}
g::say_hello(Fn("my_msg")); // prints 'Hello!'
g::say_hello(Fn("my_msg")); // prints 'Hello!'
```

View File

@ -37,12 +37,30 @@ array[0].update(); // <- call in method-call style will update 'a'
```
`&mut` is Efficient
------------------
Number of Parameters
--------------------
Native Rust methods registered with an [`Engine`] take _one additional parameter_ more than
an equivalent method coded in script, where the object is accessed via the `this` pointer instead.
The following table illustrates the differences:
| Function type | Parameters | Object reference | Function signature |
| :-----------: | :--------: | :--------------------: | :-----------------------------------------------------: |
| Native Rust | _n_ + 1 | First `&mut` parameter | `fn method<T, U, V>`<br/>`(obj: &mut T, x: U, y: V) {}` |
| Rhai script | _n_ | `this` | `fn method(x, y) {}` |
`&mut` is Efficient (Except for `ImmutableString`)
------------------------------------------------
Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone,
even when the intention is not to mutate that argument, because it avoids cloning that argument value.
For example, the `len` method of an [array] has the signature: `Fn(&mut Array) -> INT`.
The array itself is not modified in any way, but using a `&mut` parameter avoids a cloning that would
otherwise have happened if the signature were `Fn(Array) -> INT`.
For primary types that are cheap to clone (e.g. those that implement `Copy`),
including `ImmutableString`, this is not necessary.

View File

@ -63,7 +63,9 @@ cause a stack overflow in the [`Engine`], unless stopped by setting a limit for
For instance, importing itself always causes an infinite recursion:
```rust
// This file is 'hello.rhai'
--------------
| hello.rhai |
--------------
import "hello" as foo; // import itself - infinite recursion!
@ -73,11 +75,18 @@ foo::do_something();
Modules cross-referencing also cause infinite recursion:
```rust
// This file is 'hello.rhai' - references 'world.rhai'
--------------
| hello.rhai |
--------------
import "world" as foo;
foo::do_something();
// This file is 'world.rhai' - references 'hello.rhai'
--------------
| world.rhai |
--------------
import "hello" as bar;
bar::do_something_else();
```

View File

@ -9,15 +9,15 @@ and _number_ of parameters, but not parameter _types_ since all parameters are t
New definitions _overwrite_ previous definitions of the same name and number of parameters.
```rust
fn foo(x,y,z) { print("Three!!! " + x + "," + y + "," + z) }
fn foo(x,y,z) { print("Three!!! " + x + "," + y + "," + z); }
fn foo(x) { print("One! " + x) }
fn foo(x) { print("One! " + x); }
fn foo(x,y) { print("Two! " + x + "," + y) }
fn foo(x,y) { print("Two! " + x + "," + y); }
fn foo() { print("None.") }
fn foo() { print("None."); }
fn foo(x) { print("HA! NEW ONE! " + x) } // overwrites previous definition
fn foo(x) { print("HA! NEW ONE! " + x); } // overwrites previous definition
foo(1,2,3); // prints "Three!!! 1,2,3"

View File

@ -24,7 +24,7 @@ more control over what a script can (or cannot) do.
| `no_function` | Disable script-defined [functions]. |
| `no_module` | Disable loading external [modules]. |
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
| `serde` | Enable serialization/deserialization via [`serde`]. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
| `serde` | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |

View File

@ -432,7 +432,7 @@ impl Module {
/// let mut module = Module::new();
/// let hash = module.set_raw_fn("double_or_not",
/// // Pass parameter types via a slice with TypeId's
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>() ],
/// &[std::any::TypeId::of::<i64>(), std::any::TypeId::of::<bool>()],
/// // Fixed closure signature
/// |engine, lib, args| {
/// // 'args' is guaranteed to be the right length and of the correct types