bevy_reflect: Get owned fields (#5728)
# Objective
Sometimes it's useful to be able to retrieve all the fields of a container type so that they may be processed separately. With reflection, however, we typically only have access to references.
The only alternative is to "clone" the value using `Reflect::clone_value`. This, however, returns a Dynamic type in most cases. The solution there would be to use `FromReflect` instead, but this also has a problem in that it means we need to add `FromReflect` as an additional bound.
## Solution
Add a `drain` method to all container traits. This returns a `Vec<Box<dyn Reflect>>` (except for `Map` which returns `Vec<(Box<dyn Reflect>, Box<dyn Reflect>)>`).
This allows us to do things a lot simpler. For example, if we finished processing a struct and just need a particular value:
```rust
// === OLD === //
/// May or may not return a Dynamic*** value (even if `container` wasn't a `DynamicStruct`)
fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> {
container.field_at(output_index).unwrap().clone_value()
}
// === NEW === //
/// Returns _exactly_ whatever was in the given struct
fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> {
container.drain().remove(output_index).unwrap()
}
```
### Discussion
* Is `drain` the best method name? It makes sense that it "drains" all the fields and that it consumes the container in the process, but I'm open to alternatives.
---
## Changelog
* Added a `drain` method to the following traits:
* `Struct`
* `TupleStruct`
* `Tuple`
* `Array`
* `List`
* `Map`
* `Enum`
This commit is contained in:
parent
bb2303a654
commit
ecc584ff23
@ -30,6 +30,8 @@ pub trait Array: Reflect {
|
|||||||
}
|
}
|
||||||
/// Returns an iterator over the collection.
|
/// Returns an iterator over the collection.
|
||||||
fn iter(&self) -> ArrayIter;
|
fn iter(&self) -> ArrayIter;
|
||||||
|
/// Drain the elements of this array to get a vector of owned values.
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>>;
|
||||||
|
|
||||||
fn clone_dynamic(&self) -> DynamicArray {
|
fn clone_dynamic(&self) -> DynamicArray {
|
||||||
DynamicArray {
|
DynamicArray {
|
||||||
@ -246,6 +248,11 @@ impl Array for DynamicArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
|
||||||
|
self.values.into_vec()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone_dynamic(&self) -> DynamicArray {
|
fn clone_dynamic(&self) -> DynamicArray {
|
||||||
DynamicArray {
|
DynamicArray {
|
||||||
|
|||||||
@ -37,6 +37,12 @@ where
|
|||||||
index: 0,
|
index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
|
||||||
|
self.into_iter()
|
||||||
|
.map(|value| Box::new(value) as Box<dyn Reflect>)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: smallvec::Array + Send + Sync + 'static> List for SmallVec<T>
|
impl<T: smallvec::Array + Send + Sync + 'static> List for SmallVec<T>
|
||||||
|
|||||||
@ -123,6 +123,13 @@ impl<T: FromReflect> Array for Vec<T> {
|
|||||||
index: 0,
|
index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
|
||||||
|
self.into_iter()
|
||||||
|
.map(|value| Box::new(value) as Box<dyn Reflect>)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: FromReflect> List for Vec<T> {
|
impl<T: FromReflect> List for Vec<T> {
|
||||||
@ -261,6 +268,17 @@ impl<K: FromReflect + Eq + Hash, V: FromReflect> Map for HashMap<K, V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drain(self: Box<Self>) -> Vec<(Box<dyn Reflect>, Box<dyn Reflect>)> {
|
||||||
|
self.into_iter()
|
||||||
|
.map(|(key, value)| {
|
||||||
|
(
|
||||||
|
Box::new(key) as Box<dyn Reflect>,
|
||||||
|
Box::new(value) as Box<dyn Reflect>,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn clone_dynamic(&self) -> DynamicMap {
|
fn clone_dynamic(&self) -> DynamicMap {
|
||||||
let mut dynamic_map = DynamicMap::default();
|
let mut dynamic_map = DynamicMap::default();
|
||||||
dynamic_map.set_name(self.type_name().to_string());
|
dynamic_map.set_name(self.type_name().to_string());
|
||||||
@ -409,6 +427,13 @@ impl<T: Reflect, const N: usize> Array for [T; N] {
|
|||||||
index: 0,
|
index: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
|
||||||
|
self.into_iter()
|
||||||
|
.map(|value| Box::new(value) as Box<dyn Reflect>)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Reflect, const N: usize> Reflect for [T; N] {
|
impl<T: Reflect, const N: usize> Reflect for [T; N] {
|
||||||
|
|||||||
@ -519,6 +519,29 @@ mod tests {
|
|||||||
assert_eq!(foo, *foo2.downcast::<Foo>().unwrap());
|
assert_eq!(foo, *foo2.downcast::<Foo>().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_drain_fields() {
|
||||||
|
let array_value: Box<dyn Array> = Box::new([123_i32, 321_i32]);
|
||||||
|
let fields = array_value.drain();
|
||||||
|
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
|
||||||
|
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
|
||||||
|
|
||||||
|
let list_value: Box<dyn List> = Box::new(vec![123_i32, 321_i32]);
|
||||||
|
let fields = list_value.drain();
|
||||||
|
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
|
||||||
|
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
|
||||||
|
|
||||||
|
let tuple_value: Box<dyn Tuple> = Box::new((123_i32, 321_i32));
|
||||||
|
let fields = tuple_value.drain();
|
||||||
|
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
|
||||||
|
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
|
||||||
|
|
||||||
|
let map_value: Box<dyn Map> = Box::new(HashMap::from([(123_i32, 321_i32)]));
|
||||||
|
let fields = map_value.drain();
|
||||||
|
assert!(fields[0].0.reflect_partial_eq(&123_i32).unwrap_or_default());
|
||||||
|
assert!(fields[0].1.reflect_partial_eq(&321_i32).unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reflect_take() {
|
fn reflect_take() {
|
||||||
#[derive(Reflect, Debug, PartialEq)]
|
#[derive(Reflect, Debug, PartialEq)]
|
||||||
|
|||||||
@ -137,6 +137,10 @@ impl Array for DynamicList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
|
||||||
|
self.values
|
||||||
|
}
|
||||||
|
|
||||||
fn clone_dynamic(&self) -> DynamicArray {
|
fn clone_dynamic(&self) -> DynamicArray {
|
||||||
DynamicArray {
|
DynamicArray {
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
|
|||||||
@ -42,6 +42,9 @@ pub trait Map: Reflect {
|
|||||||
/// Returns an iterator over the key-value pairs of the map.
|
/// Returns an iterator over the key-value pairs of the map.
|
||||||
fn iter(&self) -> MapIter;
|
fn iter(&self) -> MapIter;
|
||||||
|
|
||||||
|
/// Drain the key-value pairs of this map to get a vector of owned values.
|
||||||
|
fn drain(self: Box<Self>) -> Vec<(Box<dyn Reflect>, Box<dyn Reflect>)>;
|
||||||
|
|
||||||
/// Clones the map, producing a [`DynamicMap`].
|
/// Clones the map, producing a [`DynamicMap`].
|
||||||
fn clone_dynamic(&self) -> DynamicMap;
|
fn clone_dynamic(&self) -> DynamicMap;
|
||||||
|
|
||||||
@ -226,6 +229,10 @@ impl Map for DynamicMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drain(self: Box<Self>) -> Vec<(Box<dyn Reflect>, Box<dyn Reflect>)> {
|
||||||
|
self.values
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reflect for DynamicMap {
|
impl Reflect for DynamicMap {
|
||||||
|
|||||||
@ -40,6 +40,9 @@ pub trait Tuple: Reflect {
|
|||||||
/// Returns an iterator over the values of the tuple's fields.
|
/// Returns an iterator over the values of the tuple's fields.
|
||||||
fn iter_fields(&self) -> TupleFieldIter;
|
fn iter_fields(&self) -> TupleFieldIter;
|
||||||
|
|
||||||
|
/// Drain the fields of this tuple to get a vector of owned values.
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>>;
|
||||||
|
|
||||||
/// Clones the struct into a [`DynamicTuple`].
|
/// Clones the struct into a [`DynamicTuple`].
|
||||||
fn clone_dynamic(&self) -> DynamicTuple;
|
fn clone_dynamic(&self) -> DynamicTuple;
|
||||||
}
|
}
|
||||||
@ -253,6 +256,11 @@ impl Tuple for DynamicTuple {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
|
||||||
|
self.fields
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone_dynamic(&self) -> DynamicTuple {
|
fn clone_dynamic(&self) -> DynamicTuple {
|
||||||
DynamicTuple {
|
DynamicTuple {
|
||||||
@ -451,6 +459,13 @@ macro_rules! impl_reflect_tuple {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
|
||||||
|
vec![
|
||||||
|
$(Box::new(self.$index),)*
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone_dynamic(&self) -> DynamicTuple {
|
fn clone_dynamic(&self) -> DynamicTuple {
|
||||||
let mut dyn_tuple = DynamicTuple {
|
let mut dyn_tuple = DynamicTuple {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user