Interop `Dynamic` Data with Rust ================================ {{#include ../links.md}} Create a `Dynamic` from Rust Type --------------------------------- | Rust type
`T: Clone`,
`K: Into` | Unavailable under | Use API | | ------------------------------------------------------------------ | :-----------------------: | ---------------------------- | | `INT` (`i64` or `i32`) | | `value.into()` | | `FLOAT` (`f64` or `f32`) | [`no_float`] | `value.into()` | | [`Decimal`][rust_decimal] (requires [`decimal`]) | | `value.into()` | | `bool` | | `value.into()` | | [`()`] | | `value.into()` | | [`String`][string], [`&str`][string], [`ImmutableString`] | | `value.into()` | | `char` | | `value.into()` | | [`Array`][array] | [`no_index`] | `Dynamic::from_array(value)` | | [`Blob`][BLOB] | [`no_index`] | `Dynamic::from_blob(value)` | | `Vec`, `&[T]`, `Iterator` | [`no_index`] | `value.into()` | | [`Map`][object map] | [`no_object`] | `Dynamic::from_map(value)` | | `HashMap`, `HashSet`,
`BTreeMap`, `BTreeSet` | [`no_object`] | `value.into()` | | [`INT..INT`][range], [`INT..=INT`][range] | | `value.into()` | | `Rc>` or `Arc>` | [`no_closure`] | `value.into()` | | [`Instant`][timestamp] | [`no_time`] or [`no_std`] | `value.into()` | | All types (including above) | | `Dynamic::from(value)` | Type Checking and Casting ------------------------- ~~~admonish tip.side "Tip: `try_cast` and `try_cast_result`" The `try_cast` method does not panic but returns `None` upon failure. The `try_cast_result` method also does not panic but returns the original value upon failure. ~~~ A [`Dynamic`] value's actual type can be checked via `Dynamic::is`. The `cast` method then converts the value into a specific, known type. Use `clone_cast` to clone a reference to [`Dynamic`]. ```rust let list: Array = engine.eval("...")?; // return type is 'Array' let item = list[0].clone(); // an element in an 'Array' is 'Dynamic' item.is::() == true; // 'is' returns whether a 'Dynamic' value is of a particular type let value = item.cast::(); // if the element is 'i64', this succeeds; otherwise it panics let value: i64 = item.cast(); // type can also be inferred let value = item.try_cast::()?; // 'try_cast' does not panic when the cast fails, but returns 'None' let value = list[0].clone_cast::(); // use 'clone_cast' on '&Dynamic' let value: i64 = list[0].clone_cast(); ``` Type Name and Matching Types ---------------------------- The `type_name` method gets the name of the actual type as a static string slice, which can be `match`-ed against. This is a very simple and direct way to act on a [`Dynamic`] value based on the actual type of the data value. ```rust let list: Array = engine.eval("...")?; // return type is 'Array' let item = list[0]; // an element in an 'Array' is 'Dynamic' match item.type_name() { // 'type_name' returns the name of the actual Rust type "()" => ... "i64" => ... "f64" => ... "rust_decimal::Decimal" => ... "core::ops::range::Range" => ... "core::ops::range::RangeInclusive" => ... "alloc::string::String" => ... "bool" => ... "char" => ... "rhai::FnPtr" => ... "std::time::Instant" => ... "crate::path::to::module::TestStruct" => ... : } ``` ```admonish warning.small "Always full path name" `type_name` always returns the _full_ Rust path name of the type, even when the type has been registered with a friendly name via `Engine::register_type_with_name`. This behavior is different from that of the [`type_of`][`type_of()`] function in Rhai. ``` Getting a Reference to Data --------------------------- Use `Dynamic::read_lock` and `Dynamic::write_lock` to get an immutable/mutable reference to the data inside a [`Dynamic`]. ```rust struct TheGreatQuestion { answer: i64 } let question = TheGreatQuestion { answer: 42 }; let mut value: Dynamic = Dynamic::from(question); let q_ref: &TheGreatQuestion = &*value.read_lock::().unwrap(); // ^^^^^^^^^^^^^^^^^^^^ cast to data type println!("answer = {}", q_ref.answer); // prints 42 let q_mut: &mut TheGreatQuestion = &mut *value.write_lock::().unwrap(); // ^^^^^^^^^^^^^^^^^^^^ cast to data type q_mut.answer = 0; // mutate value let value = value.cast::(); println!("new answer = {}", value.answer); // prints 0 ``` ~~~admonish question "TL;DR – Why `read_lock` and `write_lock`?" As the naming shows, something is _locked_ in order to allow accessing the data within a [`Dynamic`], and that something is a _shared value_ created by capturing 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, this API de-sugars to a simple reference cast. In other words, there is no locking and reference counting overhead for the vast majority of non-shared values. If the value _is_ a shared value, then it is first _locked_ and the returned _lock guard_ allows access to the underlying value in the specified type. ~~~ Methods and Traits ------------------ The following methods are available when working with [`Dynamic`]: | Method | Not available under | Return type | Description | | --------------- | :-------------------------: | :-----------------------: | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `type_name` | | `&str` | name of the value's type | | `into_shared` | [`no_closure`] | [`Dynamic`] | turn the value into a _shared_ value | | `flatten_clone` | | [`Dynamic`] | clone the value (a _shared_ value, if any, is cloned into a separate copy) | | `flatten` | | [`Dynamic`] | clone the value into a separate copy if it is _shared_ and there are multiple outstanding references, otherwise _shared_ values are turned unshared | | `read_lock` | [`no_closure`] (pass thru') | `Option<` _guard to_ `T>` | lock the value for reading | | `write_lock` | [`no_closure`] (pass thru') | `Option<` _guard to_ `T>` | lock the value exclusively for writing | | `deep_scan` | | | recursively scan for [`Dynamic`] values (e.g. items inside an [array] or [object map], or [curried arguments][currying] in a [function pointer]) | ### Constructor instance methods | Method | Not available under | Value type | Data type | | ---------------- | :-----------------------: | :---------------------------------------------------------: | :-----------------------: | | `from_bool` | | `bool` | `bool` | | `from_int` | | `INT` | integer number | | `from_float` | [`no_float`] | `FLOAT` | floating-point number | | `from_decimal` | non-[`decimal`] | [`Decimal`][rust_decimal] | [`Decimal`][rust_decimal] | | `from_str` | | `&str` | [string] | | `from_char` | | `char` | [character] | | `from_array` | [`no_index`] | `Vec` | [array] | | `from_blob` | [`no_index`] | `Vec` | [BLOB] | | `from_map` | [`no_object`] | `Map` | [object map] | | `from_timestamp` | [`no_time`] or [`no_std`] | `std::time::Instant` ([`instant::Instant`] if [WASM] build) | [timestamp] | | `from` | | `T` | [custom type] | ### Detection methods | Method | Not available under | Return type | Description | | -------------- | :-----------------------: | :---------: | ---------------------------------------------------------------------- | | `is` | | `bool` | is the value of type `T`? | | `is_variant` | | `bool` | is the value a trait object (i.e. not one of Rhai's [standard types])? | | `is_read_only` | | `bool` | is the value [constant]? A [constant] value should not be modified. | | `is_shared` | [`no_closure`] | `bool` | is the value _shared_ via a [closure]? | | `is_locked` | [`no_closure`] | `bool` | is the value _shared_ and locked (i.e. currently being read)? | | `is_unit` | | `bool` | is the value [`()`]? | | `is_int` | | `bool` | is the value an integer? | | `is_float` | [`no_float`] | `bool` | is the value a floating-point number? | | `is_decimal` | non-[`decimal`] | `bool` | is the value a [`Decimal`][rust_decimal]? | | `is_bool` | | `bool` | is the value a `bool`? | | `is_char` | | `bool` | is the value a [character]? | | `is_string` | | `bool` | is the value a [string]? | | `is_array` | [`no_index`] | `bool` | is the value an [array]? | | `is_blob` | [`no_index`] | `bool` | is the value a [BLOB]? | | `is_map` | [`no_object`] | `bool` | is the value an [object map]? | | `is_timestamp` | [`no_time`] or [`no_std`] | `bool` | is the value a [timestamp]? | ### Casting methods The following methods cast a [`Dynamic`] into a specific type: | Method | Not available under | Return type (error is name of actual type if `&str`) | | ------------------------- | :-----------------: | :------------------------------------------------------------------------: | | `cast` | | `T` (panics on failure) | | `try_cast` | | `Option` | | `try_cast_result` | | `Result` | | `clone_cast` | | cloned copy of `T` (panics on failure) | | `as_unit` | | `Result<(), &str>` | | `as_int` | | `Result` | | `as_float` | [`no_float`] | `Result` | | `as_decimal` | non-[`decimal`] | [`Result`][rust_decimal] | | `as_bool` | | `Result` | | `as_char` | | `Result` | | `as_immutable_string_ref` | | [`Result, &str>`][`ImmutableString`] | | `as_immutable_string_mut` | | [`Result, &str>`][`ImmutableString`] | | `as_array_ref` | [`no_index`] | [`Result, &str>`][array] | | `as_array_mut` | [`no_index`] | [`Result, &str>`][array] | | `as_blob_ref` | [`no_index`] | [`Result, &str>`][BLOB] | | `as_blob_mut` | [`no_index`] | [`Result, &str>`][BLOB] | | `as_map_ref` | [`no_object`] | [`Result, &str>`][object map] | | `as_map_mut` | [`no_object`] | [`Result, &str>`][object map] | | `into_string` | | `Result` | | `into_immutable_string` | | [`Result`][`ImmutableString`] | | `into_array` | [`no_index`] | [`Result`][array] | | `into_blob` | [`no_index`] | [`Result`][BLOB] | | `into_typed_array` | [`no_index`] | `Result, &str>` | ### Constructor traits The following constructor traits are implemented for [`Dynamic`] where `T: Clone`: | Trait | Not available under | Data type | | ------------------------------------------------------------------------------ | :----------------------------: | :-----------------------: | | `From<()>` | | `()` | | `From` | | integer number | | `From` | [`no_float`] | floating-point number | | `From` | non-[`decimal`] | [`Decimal`][rust_decimal] | | `From` | | `bool` | | `From>`
e.g. `From`, `From<&str>` | | [`ImmutableString`] | | `From` | | [character] | | `From>` | [`no_index`] | [array] | | `From<&[T]>` | [`no_index`] | [array] | | `From, T>>`
e.g. `From>` | [`no_object`] | [object map] | | `From>>`
e.g. `From>` | [`no_object`] | [object map] | | `From, T>>`
e.g. `From>` | [`no_object`] or [`no_std`] | [object map] | | `From>>`
e.g. `From>` | [`no_object`] or [`no_std`] | [object map] | | `From` | | [function pointer] | | `From` | [`no_time`] or [`no_std`] | [timestamp] | | `From>>` | [`sync`] or [`no_closure`] | [`Dynamic`] | | `From>>` ([`sync`]) | non-[`sync`] or [`no_closure`] | [`Dynamic`] | | `FromIterator>` | [`no_index`] | [array] |