Add docs for closures.
This commit is contained in:
parent
3d6c83c6d8
commit
4878a69503
@ -23,7 +23,7 @@ smallvec = { version = "1.4.1", default-features = false }
|
|||||||
[features]
|
[features]
|
||||||
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
|
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
|
||||||
default = []
|
default = []
|
||||||
plugins = []
|
plugins = [] # custom plugins support
|
||||||
unchecked = [] # unchecked arithmetic
|
unchecked = [] # unchecked arithmetic
|
||||||
sync = [] # restrict to only types that implement Send + Sync
|
sync = [] # restrict to only types that implement Send + Sync
|
||||||
no_optimize = [] # no script optimizer
|
no_optimize = [] # no script optimizer
|
||||||
|
@ -9,7 +9,7 @@ This version adds:
|
|||||||
* Binding the `this` pointer in a function pointer `call`.
|
* Binding the `this` pointer in a function pointer `call`.
|
||||||
* Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions.
|
* Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions.
|
||||||
* Currying of function pointers.
|
* Currying of function pointers.
|
||||||
* Auto-currying of anonymous functions.
|
* Closures - auto-currying of anonymous functions to capture shared variables from the external scope.
|
||||||
* Capturing call scope via `func!(...)` syntax.
|
* Capturing call scope via `func!(...)` syntax.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
@ -21,7 +21,7 @@ New features
|
|||||||
* Anonymous functions are supported in the syntax of a Rust closure, e.g. `|x, y, z| x + y - z`.
|
* Anonymous functions are supported in the syntax of a Rust closure, e.g. `|x, y, z| x + y - z`.
|
||||||
* Custom syntax now works even without the `internals` feature.
|
* Custom syntax now works even without the `internals` feature.
|
||||||
* Currying of function pointers is supported via the new `curry` keyword.
|
* Currying of function pointers is supported via the new `curry` keyword.
|
||||||
* Automatic currying of anonymous functions to capture environment variables.
|
* Automatic currying of anonymous functions to capture shared variables from the external scope.
|
||||||
* Capturing of the calling scope for function call via the `func!(...)` syntax.
|
* Capturing of the calling scope for function call via the `func!(...)` syntax.
|
||||||
* `Module::set_indexer_get_set_fn` is added as a shorthand of both `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn`.
|
* `Module::set_indexer_get_set_fn` is added as a shorthand of both `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn`.
|
||||||
* New `unicode-xid-ident` feature to allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) for identifiers.
|
* New `unicode-xid-ident` feature to allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) for identifiers.
|
||||||
|
@ -79,7 +79,7 @@ The Rhai Scripting Language
|
|||||||
4. [Function Pointers](language/fn-ptr.md)
|
4. [Function Pointers](language/fn-ptr.md)
|
||||||
5. [Anonymous Functions](language/fn-anon.md)
|
5. [Anonymous Functions](language/fn-anon.md)
|
||||||
6. [Currying](language/fn-curry.md)
|
6. [Currying](language/fn-curry.md)
|
||||||
7. [Capturing External Variables](language/fn-closure.md)
|
7. [Closures](language/fn-closure.md)
|
||||||
16. [Print and Debug](language/print-debug.md)
|
16. [Print and Debug](language/print-debug.md)
|
||||||
17. [Modules](language/modules/index.md)
|
17. [Modules](language/modules/index.md)
|
||||||
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
||||||
|
@ -37,6 +37,8 @@ Dynamic
|
|||||||
|
|
||||||
* Dynamic dispatch via [function pointers] with additional support for [currying].
|
* Dynamic dispatch via [function pointers] with additional support for [currying].
|
||||||
|
|
||||||
|
* Closures via [automatic currying] with capturing shared variables from the external scope.
|
||||||
|
|
||||||
* Some support for [object-oriented programming (OOP)][OOP].
|
* Some support for [object-oriented programming (OOP)][OOP].
|
||||||
|
|
||||||
Safe
|
Safe
|
||||||
|
@ -55,5 +55,6 @@ WARNING - NOT Real Closures
|
|||||||
|
|
||||||
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
|
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
|
||||||
**not** real closures.
|
**not** real closures.
|
||||||
In particular, they capture their execution environment via [automatic currying][capture],
|
|
||||||
unless the [`no_closure`] feature is turned on.
|
In particular, they capture their execution environment via [automatic currying]
|
||||||
|
(disabled via [`no_closure`]).
|
||||||
|
@ -16,6 +16,8 @@ it raises an evaluation error.
|
|||||||
It is possible, through a special syntax, to capture the calling scope - i.e. the scope
|
It is possible, through a special syntax, to capture the calling scope - i.e. the scope
|
||||||
that makes the function call - and access variables defined there.
|
that makes the function call - and access variables defined there.
|
||||||
|
|
||||||
|
Capturing can be disabled via the [`no_closure`] feature.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined
|
fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined
|
||||||
x += y; // 'x' is modified in this function
|
x += y; // 'x' is modified in this function
|
||||||
@ -47,7 +49,7 @@ f.call!(41); // <- syntax error: capturing is not allowed in method-c
|
|||||||
No Mutations
|
No Mutations
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Variables in the calling scope are accessed as copies.
|
Variables in the calling scope are captured as copies.
|
||||||
Changes to them do not reflect back to the calling scope.
|
Changes to them do not reflect back to the calling scope.
|
||||||
|
|
||||||
Rhai functions remain _pure_ in the sense that they can never mutate their environment.
|
Rhai functions remain _pure_ in the sense that they can never mutate their environment.
|
||||||
@ -56,7 +58,10 @@ Rhai functions remain _pure_ in the sense that they can never mutate their envir
|
|||||||
Caveat Emptor
|
Caveat Emptor
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Functions relying on the calling scope is a _Very Bad Idea™_ because it makes code almost impossible
|
Functions relying on the calling scope is often a _Very Bad Idea™_ because it makes code
|
||||||
to reason and maintain, as their behaviors are volatile and unpredictable.
|
almost impossible to reason and maintain, as their behaviors are volatile and unpredictable.
|
||||||
|
|
||||||
This usage should be at the last resort.
|
They behave more like macros that are expanded inline than actual function calls, thus the
|
||||||
|
syntax is also similar to Rust's macro invocations.
|
||||||
|
|
||||||
|
This usage should be at the last resort. YOU HAVE BEEN WARNED.
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
Capture External Variables via Automatic Currying
|
Simulating Closures
|
||||||
================================================
|
===================
|
||||||
|
|
||||||
{{#include ../links.md}}
|
{{#include ../links.md}}
|
||||||
|
|
||||||
Poor Man's Closures
|
Capture External Variables via Automatic Currying
|
||||||
-------------------
|
------------------------------------------------
|
||||||
|
|
||||||
Since [anonymous functions] de-sugar to standard function definitions, they retain all the behaviors of
|
Since [anonymous functions] de-sugar to standard function definitions, they retain all the behaviors of
|
||||||
Rhai functions, including being _pure_, having no access to external variables.
|
Rhai functions, including being _pure_, having no access to external variables.
|
||||||
@ -15,13 +15,23 @@ is created.
|
|||||||
|
|
||||||
Variables that are accessible during the time the [anonymous function] is created can be captured,
|
Variables that are accessible during the time the [anonymous function] is created can be captured,
|
||||||
as long as they are not shadowed by local variables defined within the function's scope.
|
as long as they are not shadowed by local variables defined within the function's scope.
|
||||||
The captured variables are automatically converted into reference-counted shared values.
|
|
||||||
|
The captured variables are automatically converted into **reference-counted shared values**
|
||||||
|
(`Rc<RefCell<Dynamic>>` in normal builds, `Arc<RwLock<Dynamic>>` in [`sync`] builds).
|
||||||
|
|
||||||
|
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 that hold them
|
||||||
|
go out of scope and no longer exist.
|
||||||
|
|
||||||
|
Use the `is_shared` function to check whether a particular value is a shared value.
|
||||||
|
|
||||||
|
Automatic currying can be turned off via the [`no_closure`] feature.
|
||||||
|
|
||||||
|
|
||||||
New Parameters For Captured Variables
|
Actual Implementation
|
||||||
------------------------------------
|
---------------------
|
||||||
|
|
||||||
In actual implementation, this de-sugars to:
|
The actual implementation de-sugars to:
|
||||||
|
|
||||||
1. Keeping track of what variables are accessed inside the anonymous function,
|
1. Keeping track of what variables are accessed inside the anonymous function,
|
||||||
|
|
||||||
@ -29,32 +39,148 @@ In actual implementation, this de-sugars to:
|
|||||||
|
|
||||||
3. The variable is added to the parameters list of the anonymous function, at the front.
|
3. The variable is added to the parameters list of the anonymous function, at the front.
|
||||||
|
|
||||||
4. The variable is then turned into a reference-counted shared value.
|
4. The variable is then converted into a **reference-counted shared value**.
|
||||||
|
|
||||||
|
An [anonymous function] which captures an external variable is the only way to create a reference-counted shared value in Rhai.
|
||||||
|
|
||||||
5. The shared value is then [curried][currying] into the [function pointer] itself, essentially carrying a reference to that shared value and inserting it into future calls of the function.
|
5. The shared value is then [curried][currying] into the [function pointer] itself, essentially carrying a reference to that shared value and inserting it into future calls of the function.
|
||||||
|
|
||||||
Automatic currying can be turned off via the [`no_closure`] feature.
|
This process is called _Automatic Currying_, and is the mechanism through which Rhai simulates normal closures.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let x = 1;
|
let x = 1; // a normal variable
|
||||||
|
|
||||||
let f = |y| x + y; // variable 'x' is auto-curried (captured) into 'f'
|
let f = |y| x + y; // variable 'x' is auto-curried (captured) into 'f'
|
||||||
// 'x' is converted into a shared value
|
|
||||||
|
|
||||||
x = 40; // 'x' can be changed
|
x.is_shared() == true; // 'x' is now a shared value!
|
||||||
|
|
||||||
|
x = 40; // changing 'x'...
|
||||||
|
|
||||||
f.call(2) == 42; // the value of 'x' is 40 because 'x' is shared
|
f.call(2) == 42; // the value of 'x' is 40 because 'x' is shared
|
||||||
|
|
||||||
// The above de-sugars into this:
|
// The above de-sugars into this:
|
||||||
fn anon$1001(x, y) { x + y } // parameter 'x' is inserted
|
fn anon$1001(x, y) { x + y } // parameter 'x' is inserted
|
||||||
|
|
||||||
make_shared(x); // convert 'x' into a shared value
|
make_shared(x); // convert variable 'x' into a shared value
|
||||||
|
|
||||||
let f = Fn("anon$1001").curry(x); // shared 'x' is curried
|
let f = Fn("anon$1001").curry(x); // shared 'x' is curried
|
||||||
|
|
||||||
f.call(2) == 42;
|
f.call(2) == 42;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
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 scope variables in closures.
|
||||||
|
|
||||||
|
It prints `9`, `9`, `9`, ... `9`, `9`, not `0`, `1`, `2`, ... `8`, `9`, because there is
|
||||||
|
ever only one captured variable, and all ten closures capture the _same_ variable.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let funcs = [];
|
||||||
|
|
||||||
|
for i in range(0, 10) {
|
||||||
|
funcs.push(|| print(i)); // the for loop variable 'i' is captured
|
||||||
|
}
|
||||||
|
|
||||||
|
funcs.len() == 10; // 10 closures stored in the array
|
||||||
|
|
||||||
|
funcs[0].type_of() == "Fn"; // make sure these are closures
|
||||||
|
|
||||||
|
for f in funcs {
|
||||||
|
f.call(); // all the references to 'i' are the same variable!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Therefore - Be Careful to Prevent Data Races
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
Rust does not have data races, but that doesn't mean Rhai doesn't.
|
||||||
|
|
||||||
|
Avoid performing a method call on a captured shared variable (which essentially takes a
|
||||||
|
mutable reference to the shared object) while using that same variable 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;
|
||||||
|
|
||||||
|
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'
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Data Races in `sync` Builds Can Become Deadlocks
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Under the [`sync`] feature, shared values are guarded with a `RwLock`, meaning that data race
|
||||||
|
conditions no longer raise an error.
|
||||||
|
|
||||||
|
Instead, they wait endlessly for the `RwLock` to be freed, and thus can become deadlocks.
|
||||||
|
|
||||||
|
On the other hand, since the same thread (i.e. the [`Engine`] thread) that is holding the lock
|
||||||
|
is attempting to read it again, this may also [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1)
|
||||||
|
depending on the O/S.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = 20;
|
||||||
|
|
||||||
|
let f = |a| this += x + a; // 'x' is captured in this closure
|
||||||
|
|
||||||
|
// Under `sync`, the following may wait forever, or may panic,
|
||||||
|
// because 'x' is locked as the `this` pointer but also accessed
|
||||||
|
// via a captured shared value.
|
||||||
|
x.call(f, 2);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
TL;DR
|
||||||
|
-----
|
||||||
|
|
||||||
|
### Q: Why are closures implemented as automatic currying?
|
||||||
|
|
||||||
|
In concept, a closure _closes_ over captured variables from the outer scope - that's why
|
||||||
|
they are called _closures_. When this happen, a typical language implementation hoists
|
||||||
|
those variables that are captured away from the stack frame and into heap-allocated storage.
|
||||||
|
This is because those variables may be needed after the stack frame goes away.
|
||||||
|
|
||||||
|
These heap-allocated captured variables only go away when all the closures that need them
|
||||||
|
are finished with them. A garbage collector makes this trivial to implement - they are
|
||||||
|
automatically collected as soon as all closures needing them are destroyed.
|
||||||
|
|
||||||
|
In Rust, this can be done by reference counting instead, with the potential pitfall of creating
|
||||||
|
reference loops that will prevent those variables from being deallocated forever.
|
||||||
|
Rhai avoids this by clone-copying most data values, so reference loops are hard to create.
|
||||||
|
|
||||||
|
Rhai does the hoisting of captured variables into the heap by converting those values
|
||||||
|
into reference-counted locked values, also allocated on the heap. The process is identical.
|
||||||
|
|
||||||
|
Closures are usually implemented as a data structure containing two items:
|
||||||
|
|
||||||
|
1) A function pointer to the function body of the closure,
|
||||||
|
2) A data structure containing references to the captured shared variables on the heap.
|
||||||
|
|
||||||
|
Usually a language implementation passes the structure containing references to captured
|
||||||
|
shared variables into the function pointer, the function body taking this data structure
|
||||||
|
as an additional parameter.
|
||||||
|
|
||||||
|
This is essentially what Rhai does, except that Rhai passes each variable individually
|
||||||
|
as separate parameters to the function, instead of creating a structure and passing that
|
||||||
|
structure as a single parameter. This is the only difference.
|
||||||
|
|
||||||
|
Therefore, in most languages, essentially all closures are implemented as automatic currying of
|
||||||
|
shared variables hoisted into the heap, automatically passing those variables as parameters into
|
||||||
|
the function. Rhai just brings this directly up to the front.
|
||||||
|
@ -33,7 +33,7 @@ curried.call(2) == 42; // <- de-sugars to 'func.call(21, 2)'
|
|||||||
Automatic Currying
|
Automatic Currying
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
[Anonymous functions] defined via a closure syntax _capture_ the _values_ of external variables
|
[Anonymous functions] defined via a closure syntax _capture_ external variables
|
||||||
that are not shadowed inside the function's scope.
|
that are not shadowed inside the function's scope.
|
||||||
|
|
||||||
This is accomplished via [automatic currying].
|
This is accomplished via [automatic currying].
|
||||||
|
@ -6,7 +6,7 @@ Values and Types
|
|||||||
The following primitive types are supported natively:
|
The following primitive types are supported natively:
|
||||||
|
|
||||||
| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
|
| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
|
||||||
| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | ----------------------- |
|
| -------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | ----------------------- |
|
||||||
| **Integer number** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ | `"i32"`, `"u64"` etc. | `"42"`, `"123"` etc. |
|
| **Integer number** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ | `"i32"`, `"u64"` etc. | `"42"`, `"123"` etc. |
|
||||||
| **Floating-point number** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
|
| **Floating-point number** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
|
||||||
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
|
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
|
||||||
@ -17,7 +17,7 @@ The following primitive types are supported natively:
|
|||||||
| **[Timestamp]** (implemented in the [`BasicTimePackage`][packages], disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`] if [WASM] build) | `"timestamp"` | `"<timestamp>"` |
|
| **[Timestamp]** (implemented in the [`BasicTimePackage`][packages], disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`] if [WASM] build) | `"timestamp"` | `"<timestamp>"` |
|
||||||
| **[Function pointer]** | `rhai::FnPtr` | `Fn` | `"Fn(foo)"` |
|
| **[Function pointer]** | `rhai::FnPtr` | `Fn` | `"Fn(foo)"` |
|
||||||
| **[`Dynamic`] value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
| **[`Dynamic`] value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
||||||
| **Shared value** (a reference-counted, shared [`Dynamic`] value) | | _the actual type_ | _actual value_ |
|
| **Shared value** (a reference-counted, shared [`Dynamic`] value, created via [automatic currying], disabled with [`no_closure`]) | | _the actual type_ | _actual value_ |
|
||||||
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
||||||
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
||||||
| **Nothing/void/nil/null/Unit** (or whatever it is called) | `()` | `"()"` | `""` _(empty string)_ |
|
| **Nothing/void/nil/null/Unit** (or whatever it is called) | `()` | `"()"` | `""` _(empty string)_ |
|
||||||
|
@ -79,8 +79,10 @@
|
|||||||
[function pointer]: {{rootUrl}}/language/fn-ptr.md
|
[function pointer]: {{rootUrl}}/language/fn-ptr.md
|
||||||
[function pointers]: {{rootUrl}}/language/fn-ptr.md
|
[function pointers]: {{rootUrl}}/language/fn-ptr.md
|
||||||
[currying]: {{rootUrl}}/language/fn-curry.md
|
[currying]: {{rootUrl}}/language/fn-curry.md
|
||||||
[capture]: {{rootUrl}}/language/fn-closure.md
|
[capture]: {{rootUrl}}/language/fn-capture.md
|
||||||
[automatic currying]: {{rootUrl}}/language/fn-closure.md
|
[automatic currying]: {{rootUrl}}/language/fn-closure.md
|
||||||
|
[closure]: {{rootUrl}}/language/fn-closure.md
|
||||||
|
[closures]: {{rootUrl}}/language/fn-closure.md
|
||||||
[function namespace]: {{rootUrl}}/language/fn-namespaces.md
|
[function namespace]: {{rootUrl}}/language/fn-namespaces.md
|
||||||
[function namespaces]: {{rootUrl}}/language/fn-namespaces.md
|
[function namespaces]: {{rootUrl}}/language/fn-namespaces.md
|
||||||
[anonymous function]: {{rootUrl}}/language/fn-anon.md
|
[anonymous function]: {{rootUrl}}/language/fn-anon.md
|
||||||
|
@ -35,12 +35,12 @@ engine.register_raw_fn(
|
|||||||
// Therefore, get a '&mut' reference to the first argument _last_.
|
// Therefore, get a '&mut' reference to the first argument _last_.
|
||||||
// Alternatively, use `args.split_at_mut(1)` etc. to split the slice first.
|
// Alternatively, use `args.split_at_mut(1)` etc. to split the slice first.
|
||||||
|
|
||||||
let y: i64 = *args[1].downcast_ref::<i64>() // get a reference to the second argument
|
let y: i64 = *args[1].read_lock::<i64>() // get a reference to the second argument
|
||||||
.unwrap(); // then copying it because it is a primary type
|
.unwrap(); // then copying it because it is a primary type
|
||||||
|
|
||||||
let y: i64 = std::mem::take(args[1]).cast::<i64>(); // alternatively, directly 'consume' it
|
let y: i64 = std::mem::take(args[1]).cast::<i64>(); // alternatively, directly 'consume' it
|
||||||
|
|
||||||
let x: &mut i64 = args[0].downcast_mut::<i64>() // get a '&mut' reference to the
|
let x: &mut i64 = args[0].write_lock::<i64>() // get a '&mut' reference to the
|
||||||
.unwrap(); // first argument
|
.unwrap(); // first argument
|
||||||
|
|
||||||
*x += y; // perform the action
|
*x += y; // perform the action
|
||||||
@ -85,11 +85,11 @@ Extract Arguments
|
|||||||
To extract an argument from the `args` parameter (`&mut [&mut Dynamic]`), use the following:
|
To extract an argument from the `args` parameter (`&mut [&mut Dynamic]`), use the following:
|
||||||
|
|
||||||
| Argument type | Access (`n` = argument position) | Result |
|
| Argument type | Access (`n` = argument position) | Result |
|
||||||
| ------------------------------ | -------------------------------------- | ---------------------------------------------------------- |
|
| ------------------------------ | ------------------------------------- | ---------------------------------------------------------- |
|
||||||
| [Primary type][standard types] | `args[n].clone().cast::<T>()` | Copy of value. |
|
| [Primary type][standard types] | `args[n].clone().cast::<T>()` | Copy of value. |
|
||||||
| Custom type | `args[n].downcast_ref::<T>().unwrap()` | Immutable reference to value. |
|
| Custom type | `args[n].read_lock::<T>().unwrap()` | Immutable reference to value. |
|
||||||
| Custom type (consumed) | `std::mem::take(args[n]).cast::<T>()` | The _consumed_ value.<br/>The original value becomes `()`. |
|
| Custom type (consumed) | `std::mem::take(args[n]).cast::<T>()` | The _consumed_ value.<br/>The original value becomes `()`. |
|
||||||
| `this` object | `args[0].downcast_mut::<T>().unwrap()` | Mutable reference to value. |
|
| `this` object | `args[0].write_lock::<T>().unwrap()` | Mutable reference to value. |
|
||||||
|
|
||||||
When there is a mutable reference to the `this` object (i.e. the first argument),
|
When there is a mutable reference to the `this` object (i.e. the first argument),
|
||||||
there can be no other immutable references to `args`, otherwise the Rust borrow checker will complain.
|
there can be no other immutable references to `args`, otherwise the Rust borrow checker will complain.
|
||||||
@ -156,5 +156,5 @@ let this_ptr = first[0].downcast_mut::<A>().unwrap();
|
|||||||
|
|
||||||
// Immutable reference to the second value parameter
|
// Immutable reference to the second value parameter
|
||||||
// This can be mutable but there is no point because the parameter is passed by value
|
// This can be mutable but there is no point because the parameter is passed by value
|
||||||
let value = rest[0].downcast_ref::<B>().unwrap();
|
let value_ref = rest[0].read_lock::<B>().unwrap();
|
||||||
```
|
```
|
||||||
|
@ -23,7 +23,7 @@ more control over what a script can (or cannot) do.
|
|||||||
| `no_object` | Disable support for [custom types] and [object maps]. |
|
| `no_object` | Disable support for [custom types] and [object maps]. |
|
||||||
| `no_function` | Disable script-defined [functions]. |
|
| `no_function` | Disable script-defined [functions]. |
|
||||||
| `no_module` | Disable loading external [modules]. |
|
| `no_module` | Disable loading external [modules]. |
|
||||||
| `no_closure` | Disable [capturing][capture] external variables in [anonymous functions] to simulate _closures_, or [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
|
| `no_closure` | Disable [capturing][automatic currying] external variables in [anonymous functions] to simulate _closures_, or [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
|
||||||
| `no_std` | Build for `no-std` (implies `no_closure`). Notice that additional dependencies will be pulled in to replace `std` features. |
|
| `no_std` | Build for `no-std` (implies `no_closure`). 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. |
|
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
|
||||||
|
24
src/any.rs
24
src/any.rs
@ -283,9 +283,9 @@ impl Dynamic {
|
|||||||
|
|
||||||
/// Get the TypeId of the value held by this `Dynamic`.
|
/// Get the TypeId of the value held by this `Dynamic`.
|
||||||
///
|
///
|
||||||
/// # Panics and Deadlocks When Value is Shared
|
/// # Panics or Deadlocks When Value is Shared
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock.
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
pub fn type_id(&self) -> TypeId {
|
pub fn type_id(&self) -> TypeId {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
@ -313,9 +313,9 @@ impl Dynamic {
|
|||||||
|
|
||||||
/// Get the name of the type of the value held by this `Dynamic`.
|
/// Get the name of the type of the value held by this `Dynamic`.
|
||||||
///
|
///
|
||||||
/// # Panics and Deadlocks When Value is Shared
|
/// # Panics or Deadlocks When Value is Shared
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock.
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
pub fn type_name(&self) -> &'static str {
|
pub fn type_name(&self) -> &'static str {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
@ -621,9 +621,9 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if types mismatched.
|
/// Returns `None` if types mismatched.
|
||||||
///
|
///
|
||||||
/// # Panics and Deadlocks
|
/// # Panics or Deadlocks
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock.
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
///
|
///
|
||||||
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
||||||
@ -744,12 +744,12 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if types mismatched.
|
/// Returns `None` if types mismatched.
|
||||||
///
|
///
|
||||||
/// # Panics and Deadlocks
|
/// # Panics or Deadlocks
|
||||||
///
|
///
|
||||||
/// Panics if the cast fails (e.g. the type of the actual value is not the
|
/// Panics if the cast fails (e.g. the type of the actual value is not the
|
||||||
/// same as the specified type).
|
/// same as the specified type).
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock.
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
///
|
///
|
||||||
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
||||||
@ -817,9 +817,9 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
///
|
///
|
||||||
/// # Panics and Deadlocks When Value is Shared
|
/// # Panics or Deadlocks When Value is Shared
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock.
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||||
@ -852,9 +852,9 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if the cast fails.
|
/// Returns `None` if the cast fails.
|
||||||
///
|
///
|
||||||
/// # Panics and Deadlocks When Value is Shared
|
/// # Panics or Deadlocks When Value is Shared
|
||||||
///
|
///
|
||||||
/// Under the `sync` feature, this call may deadlock.
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||||
|
Loading…
Reference in New Issue
Block a user