Interop Dynamic Data with Rust

Create a Dynamic from Rust Type

Rust type
T: Clone,
K: Into<String>
Unavailable underUse API
INT (i64 or i32)value.into()
FLOAT (f64 or f32)no_floatvalue.into()
Decimal (requires decimal)value.into()
String, &str, ImmutableStringvalue.into()
Vec<T>, &[T], Iterator<T>no_indexvalue.into()
HashMap<K, T>, HashSet<K>,
BTreeMap<K, T>, BTreeSet<K>
INT..INT, INT..=INTvalue.into()
Rc<RwLock<T>> or Arc<Mutex<T>>no_closurevalue.into()
Instantno_time or no_stdvalue.into()
All types (including above)Dynamic::from(value)

Type Checking and Casting

Tip: try_cast

The try_cast method does not panic but returns None 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.

let list: Array = engine.eval("...")?;      // return type is 'Array'
let item = list[0].clone();                 // an element in an 'Array' is 'Dynamic'<i64>() == true;                   // 'is' returns whether a 'Dynamic' value is of a particular type

let value = item.cast::<i64>();             // 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::<i64>()?;        // 'try_cast' does not panic when the cast fails, but returns 'None'

let value = list[0].clone_cast::<i64>();    // 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.

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<i64>" => ...
    "core::ops::range::RangeInclusive<i64>" => ...
    "alloc::string::String" => ...
    "bool" => ...
    "char" => ...
    "rhai::FnPtr" => ...
    "std::time::Instant" => ...
    "crate::path::to::module::TestStruct" => ...

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 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.

struct TheGreatQuestion {
    answer: i64

let question = TheGreatQuestion { answer: 42 };

let mut value: Dynamic = Dynamic::from(question);

let q_ref: &TheGreatQuestion =
//                       ^^^^^^^^^^^^^^^^^^^^ cast to data type

println!("answer = {}", q_ref.answer);          // prints 42

let q_mut: &mut TheGreatQuestion =
        &mut *value.write_lock::<TheGreatQuestion>().unwrap();
//                            ^^^^^^^^^^^^^^^^^^^^ cast to data type

q_mut.answer = 0;                               // mutate value

let value = value.cast::<TheGreatQuestion>();

println!("new answer = {}", value.answer);      // prints 0

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<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, 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:

MethodNot available underReturn typeDescription
type_name&strname of the value’s type
into_sharedno_closureDynamicturn the value into a shared value
flatten_cloneDynamicclone the value (a shared value, if any, is cloned into a separate copy)
flattenDynamicclone the value into a separate copy if it is shared and there are multiple outstanding references, otherwise shared values are turned unshared
read_lock<T>no_closure (pass thru’)Option< guard to T>lock the value for reading
write_lock<T>no_closure (pass thru’)Option< guard to T>lock the value exclusively for writing
deep_scanrecursively scan for Dynamic values (e.g. items inside an array or object map, or curried arguments in a function pointer)

Constructor instance methods

MethodNot available underValue typeData type
from_intINTinteger number
from_floatno_floatFLOATfloating-point number
from_mapno_objectMapobject map
from_timestampno_time or no_stdstd::time::Instant (instant::Instant if WASM build)timestamp
from<T>Tcustom type

Detection methods

MethodNot available underReturn typeDescription
is<T>boolis the value of type T?
is_variantboolis the value a trait object (i.e. not one of Rhai’s standard types)?
is_read_onlyboolis the value constant? A constant value should not be modified.
is_sharedno_closureboolis the value shared via a closure?
is_lockedno_closureboolis the value shared and locked (i.e. currently being read)?
is_unitboolis the value ()?
is_intboolis the value an integer?
is_floatno_floatboolis the value a floating-point number?
is_decimalnon-decimalboolis the value a Decimal?
is_boolboolis the value a bool?
is_charboolis the value a character?
is_stringboolis the value a string?
is_arrayno_indexboolis the value an array?
is_blobno_indexboolis the value a BLOB?
is_mapno_objectboolis the value an object map?
is_timestampno_time or no_stdboolis the value a timestamp?

Casting methods

The following methods cast a Dynamic into a specific type:

MethodNot available underReturn type (error is the actual data type)
cast<T>T (panics on failure)
clone_cast<T>cloned copy of T (panics on failure)
as_unitResult<(), &str>
as_intResult<INT, &str>
as_floatno_floatResult<FLOAT, &str>
as_decimalnon-decimalResult<Decimal, &str>
as_boolResult<bool, &str>
as_charResult<char, &str>
into_stringResult<String, &str>
into_immutable_stringResult<ImmutableString, &str>
into_arrayno_indexResult<Array, &str>
into_blobno_indexResult<Blob, &str>
into_typed_array<T>no_indexResult<Vec<T>, &str>

Constructor traits

The following constructor traits are implemented for Dynamic where T: Clone:

TraitNot available underData type
From<INT>integer number
From<FLOAT>no_floatfloating-point number
From<S: Into<ImmutableString>>
e.g. From<String>, From<&str>
From<BTreeMap<K: Into<SmartString>, T>>
e.g. From<BTreeMap<String, T>>
no_objectobject map
From<BTreeSet<K: Into<SmartString>>>
e.g. From<BTreeSet<String>>
no_objectobject map
From<HashMap<K: Into<SmartString>, T>>
e.g. From<HashMap<String, T>>
no_object or no_stdobject map
From<HashSet<K: Into<SmartString>>>
e.g. From<HashSet<String>>
no_object or no_stdobject map
From<FnPtr>function pointer
From<Instant>no_time or no_stdtimestamp
From<Rc<RefCell<Dynamic>>>sync or no_closureDynamic
From<Arc<RwLock<Dynamic>>> (sync)non-sync or no_closureDynamic
FromIterator<X: IntoIterator<Item=T>>no_indexarray