diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 59d7aff9..39610a97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: - "--features sync" - "--features no_optimize" - "--features no_float" - - "--features only_i32" + - "--tests --features only_i32" - "--features only_i64" - "--features no_index" - "--features no_object" diff --git a/doc/src/language/method.md b/doc/src/language/method.md index 290d1a06..3b8a82d4 100644 --- a/doc/src/language/method.md +++ b/doc/src/language/method.md @@ -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' ``` +**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.** + Number of Parameters -------------------- @@ -51,8 +53,8 @@ The following table illustrates the differences: | 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, even when the intention is not to mutate that argument, because it avoids cloning that argument value. diff --git a/doc/src/rust/custom.md b/doc/src/rust/custom.md index 3e6b5ca3..b2325204 100644 --- a/doc/src/rust/custom.md +++ b/doc/src/rust/custom.md @@ -83,6 +83,8 @@ engine ***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).* +**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.** + 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()")?; ``` -[`type_of()`] -------------- +`type_of()` a Custom Type +------------------------- [`type_of()`] works fine with custom types and returns the name of the type. diff --git a/doc/src/rust/getters-setters.md b/doc/src/rust/getters-setters.md index 4c44399d..7597e8d5 100644 --- a/doc/src/rust/getters-setters.md +++ b/doc/src/rust/getters-setters.md @@ -42,3 +42,5 @@ let result = engine.eval::(r#"let a = new_ts(); a.xyz = "42"; a.xyz"#)?; println!("Answer: {}", result); // prints 42 ``` + +**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.** diff --git a/doc/src/rust/register-raw.md b/doc/src/rust/register-raw.md index 5216a56d..b7069ce3 100644 --- a/doc/src/rust/register-raw.md +++ b/doc/src/rust/register-raw.md @@ -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. -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, then calls it within the same [`Engine`]. This way, a _callback_ function can be provided @@ -140,6 +141,24 @@ let result = engine.eval::(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>` (`Arc>` 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 ------------------------ @@ -152,9 +171,9 @@ to partition the slice: let (first, rest) = args.split_at_mut(1); // Mutable reference to the first parameter -let this_ptr = first[0].write_lock::().unwrap(); +let this_ptr: &mut Dynamic = &mut *first[0].write_lock::().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_ref = rest[0].read_lock::().unwrap(); +let value_ref: &Dynamic = &*rest[0].read_lock::().unwrap(); ``` diff --git a/src/serde/de.rs b/src/serde/de.rs index 843c00d2..6b0b8a53 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -136,6 +136,7 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>( impl Error for Box { fn custom(err: T) -> Self { EvalAltResult::ErrorParsing(ParseErrorType::BadInput(err.to_string()), Position::none()) + .into() } } diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 1f0d153d..f1a2ab77 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -85,9 +85,9 @@ impl DynamicSerializer { /// assert!(value.is::()); /// /// let map = value.cast::(); -/// let point = map.get("d").unwrap().downcast_ref::().unwrap(); -/// assert_eq!(*point.get("x").unwrap().downcast_ref::().unwrap(), 123.456); -/// assert_eq!(*point.get("y").unwrap().downcast_ref::().unwrap(), 999.0); +/// let point = map["d"].read_lock::().unwrap(); +/// assert_eq!(*point["x"].read_lock::().unwrap(), 123.456); +/// assert_eq!(*point["y"].read_lock::().unwrap(), 999.0); /// # } /// # Ok(()) /// # } @@ -99,7 +99,7 @@ pub fn to_dynamic(value: T) -> Result> impl Error for Box { fn custom(err: T) -> Self { - EvalAltResult::ErrorRuntime(err.to_string(), Position::none()) + EvalAltResult::ErrorRuntime(err.to_string(), Position::none()).into() } }