From b14d18934af8909e25e8e9c1b06bffedd97e8044 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 30 Mar 2021 18:57:16 +0800 Subject: [PATCH] Add Dynamic::clone_cast. --- CHANGELOG.md | 1 + src/dynamic.rs | 31 +++++++++++++++++++++++++++++++ src/module/mod.rs | 2 +- src/serde/de.rs | 40 ++++++++++++++++++++-------------------- tests/arrays.rs | 5 +++++ tests/closures.rs | 2 +- tests/maps.rs | 22 +++++++++++----------- 7 files changed, 70 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7608ccc3..d0421301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ Enhancements * Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny. * `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type. * `#[rhai_fn(return_raw)]` can now return `Result>` where `T` is any clonable type instead of `Result>`. +* `Dynamic::clone_cast` is added to simplify casting from a `&Dynamic`. Version 0.19.14 diff --git a/src/dynamic.rs b/src/dynamic.rs index fd730ef3..3c0c23d4 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1131,6 +1131,37 @@ impl Dynamic { ) }) } + /// Clone the [`Dynamic`] value and convert it into a specific type. + /// + /// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value, + /// it is cloned into a [`Dynamic`] with a normal value. + /// + /// Returns [`None`] if types mismatched. + /// + /// # 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, 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. + /// + /// # Example + /// + /// ``` + /// use rhai::Dynamic; + /// + /// let x = Dynamic::from(42_u32); + /// let y = &x; + /// + /// assert_eq!(y.clone_cast::(), 42); + /// ``` + #[inline(always)] + pub fn clone_cast(&self) -> T { + self.read_lock::().unwrap().clone() + } /// Flatten the [`Dynamic`] and clone it. /// /// If the [`Dynamic`] is not a shared value, it returns a cloned copy. diff --git a/src/module/mod.rs b/src/module/mod.rs index 824d4e72..8ae91968 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -768,7 +768,7 @@ impl Module { /// // Get the second parameter by 'consuming' it /// let double = std::mem::take(args[1]).cast::(); /// // Since it is a primary type, it can also be cheaply copied - /// let double = args[1].clone().cast::(); + /// let double = args[1].clone_cast::(); /// // Get a mutable reference to the first argument. /// let mut x = args[0].write_lock::().unwrap(); /// diff --git a/src/serde/de.rs b/src/serde/de.rs index 802a47e2..faf98d71 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -183,7 +183,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.deserialize_int(v, visitor) } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i8(x)) } } @@ -193,7 +193,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.deserialize_int(v, visitor) } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i16(x)) } } @@ -205,7 +205,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.type_error() } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i32(x)) } } @@ -217,7 +217,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.type_error() } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i64(x)) } } @@ -229,7 +229,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.type_error() } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_i128(x)) } } @@ -239,7 +239,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.deserialize_int(v, visitor) } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u8(x)) } } @@ -249,7 +249,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.deserialize_int(v, visitor) } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u16(x)) } } @@ -259,7 +259,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.deserialize_int(v, visitor) } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u32(x)) } } @@ -269,7 +269,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.deserialize_int(v, visitor) } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u64(x)) } } @@ -279,7 +279,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { self.deserialize_int(v, visitor) } else { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_u128(x)) } } @@ -288,7 +288,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { #[cfg(not(feature = "no_float"))] return self .value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x)); #[cfg(feature = "no_float")] @@ -298,7 +298,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { return self .value - .downcast_ref::() + .downclone_cast::() .and_then(|&x| x.to_f32()) .map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v)); } @@ -312,7 +312,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { #[cfg(not(feature = "no_float"))] return self .value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x)); #[cfg(feature = "no_float")] @@ -322,7 +322,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { return self .value - .downcast_ref::() + .downclone_cast::() .and_then(|&x| x.to_f64()) .map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v)); } @@ -334,12 +334,12 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { fn deserialize_char>(self, visitor: V) -> Result> { self.value - .downcast_ref::() + .downclone_cast::() .map_or_else(|| self.type_error(), |&x| visitor.visit_char(x)) } fn deserialize_str>(self, visitor: V) -> Result> { - self.value.downcast_ref::().map_or_else( + self.value.downclone_cast::().map_or_else( || self.type_error(), |x| visitor.visit_borrowed_str(x.as_str()), ) @@ -366,7 +366,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { fn deserialize_unit>(self, visitor: V) -> Result> { self.value - .downcast_ref::<()>() + .downclone_cast::<()>() .map_or_else(|| self.type_error(), |_| visitor.visit_unit()) } @@ -388,7 +388,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { fn deserialize_seq>(self, _visitor: V) -> Result> { #[cfg(not(feature = "no_index"))] - return self.value.downcast_ref::().map_or_else( + return self.value.downclone_cast::().map_or_else( || self.type_error(), |arr| _visitor.visit_seq(IterateArray::new(arr.iter())), ); @@ -416,7 +416,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { fn deserialize_map>(self, _visitor: V) -> Result> { #[cfg(not(feature = "no_object"))] - return self.value.downcast_ref::().map_or_else( + return self.value.downclone_cast::().map_or_else( || self.type_error(), |map| { _visitor.visit_map(IterateMap::new( @@ -449,7 +449,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { visitor.visit_enum(s.as_str().into_deserializer()) } else { #[cfg(not(feature = "no_object"))] - if let Some(map) = self.value.downcast_ref::() { + if let Some(map) = self.value.downclone_cast::() { let mut iter = map.iter(); let first = iter.next(); let second = iter.next(); diff --git a/tests/arrays.rs b/tests/arrays.rs index 1be7f8cb..0c154cd6 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -59,6 +59,11 @@ fn test_arrays() -> Result<(), Box> { 5 ); + let mut a = Array::new(); + a.push((42 as INT).into()); + + assert_eq!(a[0].clone_cast::(), 42); + Ok(()) } diff --git a/tests/closures.rs b/tests/closures.rs index cfa1f355..4b35e2d7 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -299,7 +299,7 @@ fn test_closures_shared_obj() -> Result<(), Box> { // Make closure let f = move |p1: TestStruct, p2: TestStruct| -> Result<(), Box> { - let action_ptr = res["action"].clone().cast::(); + let action_ptr = res["action"].clone_cast::(); let name = action_ptr.fn_name(); engine.call_fn(&mut Scope::new(), &ast, name, (p1, p2)) }; diff --git a/tests/maps.rs b/tests/maps.rs index 5892c29d..895c7310 100644 --- a/tests/maps.rs +++ b/tests/maps.rs @@ -94,9 +94,9 @@ fn test_map_assign() -> Result<(), Box> { let x = engine.eval::(r#"let x = #{a: 1, b: true, "c$": "hello"}; x"#)?; - assert_eq!(x["a"].clone().cast::(), 1); - assert_eq!(x["b"].clone().cast::(), true); - assert_eq!(x["c$"].clone().cast::(), "hello"); + assert_eq!(x["a"].clone_cast::(), 1); + assert_eq!(x["b"].clone_cast::(), true); + assert_eq!(x["c$"].clone_cast::(), "hello"); Ok(()) } @@ -107,9 +107,9 @@ fn test_map_return() -> Result<(), Box> { let x = engine.eval::(r#"#{a: 1, b: true, "c$": "hello"}"#)?; - assert_eq!(x["a"].clone().cast::(), 1); - assert_eq!(x["b"].clone().cast::(), true); - assert_eq!(x["c$"].clone().cast::(), "hello"); + assert_eq!(x["a"].clone_cast::(), 1); + assert_eq!(x["b"].clone_cast::(), true); + assert_eq!(x["c$"].clone_cast::(), "hello"); Ok(()) } @@ -152,11 +152,11 @@ fn test_map_json() -> Result<(), Box> { assert!(!map.contains_key("x")); - assert_eq!(map["a"].clone().cast::(), 1); - assert_eq!(map["b"].clone().cast::(), true); - assert_eq!(map["c"].clone().cast::(), 42); - assert_eq!(map["$d e f!"].clone().cast::(), "hello"); - assert_eq!(map["z"].clone().cast::<()>(), ()); + assert_eq!(map["a"].clone_cast::(), 1); + assert_eq!(map["b"].clone_cast::(), true); + assert_eq!(map["c"].clone_cast::(), 42); + assert_eq!(map["$d e f!"].clone_cast::(), "hello"); + assert_eq!(map["z"].clone_cast::<()>(), ()); #[cfg(not(feature = "no_index"))] {