Merge branch 'master' into plugins

This commit is contained in:
Stephen Chung 2020-08-06 23:17:20 +08:00
commit 6475e4e20e
7 changed files with 40 additions and 14 deletions

View File

@ -22,7 +22,7 @@ jobs:
- "--features sync" - "--features sync"
- "--features no_optimize" - "--features no_optimize"
- "--features no_float" - "--features no_float"
- "--features only_i32" - "--tests --features only_i32"
- "--features only_i64" - "--features only_i64"
- "--features no_index" - "--features no_index"
- "--features no_object" - "--features no_object"

View File

@ -36,6 +36,8 @@ update(array[0]); // <- 'array[0]' is an expression returning a calculated val
array[0].update(); // <- call in method-call style will update 'a' array[0].update(); // <- call in method-call style will update 'a'
``` ```
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
Number of Parameters Number of Parameters
-------------------- --------------------
@ -51,8 +53,8 @@ The following table illustrates the differences:
| Rhai script | _n_ | `this` | `fn method(x, y) {}` | | Rhai script | _n_ | `this` | `fn method(x, y) {}` |
`&mut` is Efficient (Except for `ImmutableString`) `&mut` is Efficient, Except for `ImmutableString`
------------------------------------------------ -----------------------------------------------
Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone, 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. even when the intention is not to mutate that argument, because it avoids cloning that argument value.

View File

@ -83,6 +83,8 @@ engine
***Note**: Rhai follows the convention that methods of custom types take a `&mut` first parameter ***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).* so that invoking methods can update the types. All other parameters in Rhai are passed by value (i.e. clones).*
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
Use the Custom Type in Scripts Use the Custom Type in Scripts
----------------------------- -----------------------------
@ -125,8 +127,8 @@ Under [`no_object`], however, the _method_ style of function calls
let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?; let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?;
``` ```
[`type_of()`] `type_of()` a Custom Type
------------- -------------------------
[`type_of()`] works fine with custom types and returns the name of the type. [`type_of()`] works fine with custom types and returns the name of the type.

View File

@ -42,3 +42,5 @@ let result = engine.eval::<String>(r#"let a = new_ts(); a.xyz = "42"; a.xyz"#)?;
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
``` ```
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**

View File

@ -95,10 +95,11 @@ 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.
Example - Passing a Function Pointer to a Rust Function Example - Passing a Callback to a Rust Function
------------------------------------------------------ ----------------------------------------------
The low-level API is useful when there is a need to interact with the scripting [`Engine`] within a function. The low-level API is useful when there is a need to interact with the scripting [`Engine`]
within a function.
The following example registers a function that takes a [function pointer] as an argument, The following example registers a function that takes a [function pointer] as an argument,
then calls it within the same [`Engine`]. This way, a _callback_ function can be provided then calls it within the same [`Engine`]. This way, a _callback_ function can be provided
@ -140,6 +141,24 @@ let result = engine.eval::<i64>(r#"
``` ```
TL;DR - Why `read_lock` and `write_lock`
---------------------------------------
The `Dynamic` API that casts it to a reference to a particular data type is `read_lock`
(for an immutable reference) and `write_lock` (for a mutable reference).
As the naming shows, something is _locked_ in order to allow this access, and that something
is a _shared value_ created by [capturing][automatic currying] variables from [closures].
Shared values are implemented as `Rc<RefCell<Dynamic>>` (`Arc<RwLock<Dynamic>>` under [`sync`]).
If the value is _not_ a shared value, or if running under [`no_closure`] where there is
no [capturing][automatic currying], this API de-sugars to a simple `downcast_ref` and `downcast_mut`.
If the value is a shared value, then it is first locked and the returned lock guard
then allows access to the underlying value in the specified type.
Hold Multiple References Hold Multiple References
------------------------ ------------------------
@ -152,9 +171,9 @@ to partition the slice:
let (first, rest) = args.split_at_mut(1); let (first, rest) = args.split_at_mut(1);
// Mutable reference to the first parameter // Mutable reference to the first parameter
let this_ptr = first[0].write_lock::<A>().unwrap(); let this_ptr: &mut Dynamic = &mut *first[0].write_lock::<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_ref = rest[0].read_lock::<B>().unwrap(); let value_ref: &Dynamic = &*rest[0].read_lock::<B>().unwrap();
``` ```

View File

@ -136,6 +136,7 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>(
impl Error for Box<EvalAltResult> { impl Error for Box<EvalAltResult> {
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none()) EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none())
.into()
} }
} }

View File

@ -85,9 +85,9 @@ impl DynamicSerializer {
/// assert!(value.is::<Map>()); /// assert!(value.is::<Map>());
/// ///
/// let map = value.cast::<Map>(); /// let map = value.cast::<Map>();
/// let point = map.get("d").unwrap().downcast_ref::<Map>().unwrap(); /// let point = map["d"].read_lock::<Map>().unwrap();
/// assert_eq!(*point.get("x").unwrap().downcast_ref::<f64>().unwrap(), 123.456); /// assert_eq!(*point["x"].read_lock::<f64>().unwrap(), 123.456);
/// assert_eq!(*point.get("y").unwrap().downcast_ref::<f64>().unwrap(), 999.0); /// assert_eq!(*point["y"].read_lock::<f64>().unwrap(), 999.0);
/// # } /// # }
/// # Ok(()) /// # Ok(())
/// # } /// # }
@ -99,7 +99,7 @@ pub fn to_dynamic<T: Serialize>(value: T) -> Result<Dynamic, Box<EvalAltResult>>
impl Error for Box<EvalAltResult> { impl Error for Box<EvalAltResult> {
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
EvalAltResult::ErrorRuntime(err.to_string(), Position::none()) EvalAltResult::ErrorRuntime(err.to_string(), Position::none()).into()
} }
} }