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]
|
||||
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
|
||||
default = []
|
||||
plugins = []
|
||||
plugins = [] # custom plugins support
|
||||
unchecked = [] # unchecked arithmetic
|
||||
sync = [] # restrict to only types that implement Send + Sync
|
||||
no_optimize = [] # no script optimizer
|
||||
|
@ -9,7 +9,7 @@ This version adds:
|
||||
* Binding the `this` pointer in a function pointer `call`.
|
||||
* Anonymous functions (in Rust closure syntax). Simplifies creation of single-use ad-hoc functions.
|
||||
* 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.
|
||||
|
||||
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`.
|
||||
* Custom syntax now works even without the `internals` feature.
|
||||
* 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.
|
||||
* `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.
|
||||
|
@ -79,7 +79,7 @@ The Rhai Scripting Language
|
||||
4. [Function Pointers](language/fn-ptr.md)
|
||||
5. [Anonymous Functions](language/fn-anon.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)
|
||||
17. [Modules](language/modules/index.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].
|
||||
|
||||
* Closures via [automatic currying] with capturing shared variables from the external scope.
|
||||
|
||||
* Some support for [object-oriented programming (OOP)][OOP].
|
||||
|
||||
Safe
|
||||
|
@ -55,5 +55,6 @@ WARNING - NOT Real Closures
|
||||
|
||||
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
|
||||
**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
|
||||
that makes the function call - and access variables defined there.
|
||||
|
||||
Capturing can be disabled via the [`no_closure`] feature.
|
||||
|
||||
```rust
|
||||
fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined
|
||||
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
|
||||
------------
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
-------------
|
||||
|
||||
Functions relying on the calling scope is a _Very Bad Idea™_ because it makes code almost impossible
|
||||
to reason and maintain, as their behaviors are volatile and unpredictable.
|
||||
Functions relying on the calling scope is often a _Very Bad Idea™_ because it makes code
|
||||
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}}
|
||||
|
||||
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
|
||||
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,
|
||||
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,
|
||||
|
||||
@ -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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
--------
|
||||
|
||||
```rust
|
||||
let x = 1;
|
||||
let x = 1; // a normal variable
|
||||
|
||||
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
|
||||
|
||||
// The above de-sugars into this:
|
||||
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
|
||||
|
||||
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
|
||||
------------------
|
||||
|
||||
[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.
|
||||
|
||||
This is accomplished via [automatic currying].
|
||||
|
@ -6,7 +6,7 @@ Values and Types
|
||||
The following primitive types are supported natively:
|
||||
|
||||
| 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. |
|
||||
| **Floating-point number** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
|
||||
| **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>"` |
|
||||
| **[Function pointer]** | `rhai::FnPtr` | `Fn` | `"Fn(foo)"` |
|
||||
| **[`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 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)_ |
|
||||
|
@ -79,8 +79,10 @@
|
||||
[function pointer]: {{rootUrl}}/language/fn-ptr.md
|
||||
[function pointers]: {{rootUrl}}/language/fn-ptr.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
|
||||
[closure]: {{rootUrl}}/language/fn-closure.md
|
||||
[closures]: {{rootUrl}}/language/fn-closure.md
|
||||
[function namespace]: {{rootUrl}}/language/fn-namespaces.md
|
||||
[function namespaces]: {{rootUrl}}/language/fn-namespaces.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_.
|
||||
// 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
|
||||
|
||||
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
|
||||
|
||||
*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:
|
||||
|
||||
| Argument type | Access (`n` = argument position) | Result |
|
||||
| ------------------------------ | -------------------------------------- | ---------------------------------------------------------- |
|
||||
| ------------------------------ | ------------------------------------- | ---------------------------------------------------------- |
|
||||
| [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 `()`. |
|
||||
| `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),
|
||||
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
|
||||
// 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_function` | Disable script-defined [functions]. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
|
24
src/any.rs
24
src/any.rs
@ -283,9 +283,9 @@ impl 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.
|
||||
pub fn type_id(&self) -> TypeId {
|
||||
match &self.0 {
|
||||
@ -313,9 +313,9 @@ impl 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.
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match &self.0 {
|
||||
@ -621,9 +621,9 @@ impl Dynamic {
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
||||
@ -744,12 +744,12 @@ impl Dynamic {
|
||||
///
|
||||
/// 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
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// # 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.
|
||||
#[inline(always)]
|
||||
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||
@ -852,9 +852,9 @@ impl Dynamic {
|
||||
///
|
||||
/// 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.
|
||||
#[inline(always)]
|
||||
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||
|
Loading…
Reference in New Issue
Block a user