Skip to content

Commit ecc584f

Browse files
committed
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`
1 parent bb2303a commit ecc584f

File tree

7 files changed

+87
-0
lines changed

7 files changed

+87
-0
lines changed

crates/bevy_reflect/src/array.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub trait Array: Reflect {
3030
}
3131
/// Returns an iterator over the collection.
3232
fn iter(&self) -> ArrayIter;
33+
/// Drain the elements of this array to get a vector of owned values.
34+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>>;
3335

3436
fn clone_dynamic(&self) -> DynamicArray {
3537
DynamicArray {
@@ -246,6 +248,11 @@ impl Array for DynamicArray {
246248
}
247249
}
248250

251+
#[inline]
252+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
253+
self.values.into_vec()
254+
}
255+
249256
#[inline]
250257
fn clone_dynamic(&self) -> DynamicArray {
251258
DynamicArray {

crates/bevy_reflect/src/impls/smallvec.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ where
3737
index: 0,
3838
}
3939
}
40+
41+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
42+
self.into_iter()
43+
.map(|value| Box::new(value) as Box<dyn Reflect>)
44+
.collect()
45+
}
4046
}
4147

4248
impl<T: smallvec::Array + Send + Sync + 'static> List for SmallVec<T>

crates/bevy_reflect/src/impls/std.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ impl<T: FromReflect> Array for Vec<T> {
123123
index: 0,
124124
}
125125
}
126+
127+
#[inline]
128+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
129+
self.into_iter()
130+
.map(|value| Box::new(value) as Box<dyn Reflect>)
131+
.collect()
132+
}
126133
}
127134

128135
impl<T: FromReflect> List for Vec<T> {
@@ -261,6 +268,17 @@ impl<K: FromReflect + Eq + Hash, V: FromReflect> Map for HashMap<K, V> {
261268
}
262269
}
263270

271+
fn drain(self: Box<Self>) -> Vec<(Box<dyn Reflect>, Box<dyn Reflect>)> {
272+
self.into_iter()
273+
.map(|(key, value)| {
274+
(
275+
Box::new(key) as Box<dyn Reflect>,
276+
Box::new(value) as Box<dyn Reflect>,
277+
)
278+
})
279+
.collect()
280+
}
281+
264282
fn clone_dynamic(&self) -> DynamicMap {
265283
let mut dynamic_map = DynamicMap::default();
266284
dynamic_map.set_name(self.type_name().to_string());
@@ -409,6 +427,13 @@ impl<T: Reflect, const N: usize> Array for [T; N] {
409427
index: 0,
410428
}
411429
}
430+
431+
#[inline]
432+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
433+
self.into_iter()
434+
.map(|value| Box::new(value) as Box<dyn Reflect>)
435+
.collect()
436+
}
412437
}
413438

414439
impl<T: Reflect, const N: usize> Reflect for [T; N] {

crates/bevy_reflect/src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,29 @@ mod tests {
519519
assert_eq!(foo, *foo2.downcast::<Foo>().unwrap());
520520
}
521521

522+
#[test]
523+
fn should_drain_fields() {
524+
let array_value: Box<dyn Array> = Box::new([123_i32, 321_i32]);
525+
let fields = array_value.drain();
526+
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
527+
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
528+
529+
let list_value: Box<dyn List> = Box::new(vec![123_i32, 321_i32]);
530+
let fields = list_value.drain();
531+
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
532+
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
533+
534+
let tuple_value: Box<dyn Tuple> = Box::new((123_i32, 321_i32));
535+
let fields = tuple_value.drain();
536+
assert!(fields[0].reflect_partial_eq(&123_i32).unwrap_or_default());
537+
assert!(fields[1].reflect_partial_eq(&321_i32).unwrap_or_default());
538+
539+
let map_value: Box<dyn Map> = Box::new(HashMap::from([(123_i32, 321_i32)]));
540+
let fields = map_value.drain();
541+
assert!(fields[0].0.reflect_partial_eq(&123_i32).unwrap_or_default());
542+
assert!(fields[0].1.reflect_partial_eq(&321_i32).unwrap_or_default());
543+
}
544+
522545
#[test]
523546
fn reflect_take() {
524547
#[derive(Reflect, Debug, PartialEq)]

crates/bevy_reflect/src/list.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ impl Array for DynamicList {
137137
}
138138
}
139139

140+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
141+
self.values
142+
}
143+
140144
fn clone_dynamic(&self) -> DynamicArray {
141145
DynamicArray {
142146
name: self.name.clone(),

crates/bevy_reflect/src/map.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ pub trait Map: Reflect {
4242
/// Returns an iterator over the key-value pairs of the map.
4343
fn iter(&self) -> MapIter;
4444

45+
/// Drain the key-value pairs of this map to get a vector of owned values.
46+
fn drain(self: Box<Self>) -> Vec<(Box<dyn Reflect>, Box<dyn Reflect>)>;
47+
4548
/// Clones the map, producing a [`DynamicMap`].
4649
fn clone_dynamic(&self) -> DynamicMap;
4750

@@ -226,6 +229,10 @@ impl Map for DynamicMap {
226229
}
227230
}
228231
}
232+
233+
fn drain(self: Box<Self>) -> Vec<(Box<dyn Reflect>, Box<dyn Reflect>)> {
234+
self.values
235+
}
229236
}
230237

231238
impl Reflect for DynamicMap {

crates/bevy_reflect/src/tuple.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ pub trait Tuple: Reflect {
4040
/// Returns an iterator over the values of the tuple's fields.
4141
fn iter_fields(&self) -> TupleFieldIter;
4242

43+
/// Drain the fields of this tuple to get a vector of owned values.
44+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>>;
45+
4346
/// Clones the struct into a [`DynamicTuple`].
4447
fn clone_dynamic(&self) -> DynamicTuple;
4548
}
@@ -253,6 +256,11 @@ impl Tuple for DynamicTuple {
253256
}
254257
}
255258

259+
#[inline]
260+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
261+
self.fields
262+
}
263+
256264
#[inline]
257265
fn clone_dynamic(&self) -> DynamicTuple {
258266
DynamicTuple {
@@ -451,6 +459,13 @@ macro_rules! impl_reflect_tuple {
451459
}
452460
}
453461

462+
#[inline]
463+
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
464+
vec![
465+
$(Box::new(self.$index),)*
466+
]
467+
}
468+
454469
#[inline]
455470
fn clone_dynamic(&self) -> DynamicTuple {
456471
let mut dyn_tuple = DynamicTuple {

0 commit comments

Comments
 (0)