bevy_reflect: Decouple List and Array traits (#7467)

# Objective

Resolves #7121

## Solution

Decouples `List` and `Array` by removing `Array` as a supertrait of `List`. Additionally, similar methods from `Array` have been added to `List` so that their usages can remain largely unchanged.

#### Possible Alternatives

##### `Sequence`

My guess for why we originally made `List` a subtrait of `Array` is that they share a lot of common operations. We could potentially move these overlapping methods to a `Sequence` (name taken from #7059) trait and make that a supertrait of both. This would allow functions to contain logic that simply operates on a sequence rather than "list vs array".

However, this means that we'd need to add methods for converting to a `dyn Sequence`. It also might be confusing since we wouldn't add a `ReflectRef::Sequence` or anything like that. Is such a trait worth adding (either in this PR or a followup one)? 

---

## Changelog

- Removed `Array` as supertrait of `List`
  - Added methods to `List` that were previously provided by `Array`

## Migration Guide

The `List` trait is no longer dependent on `Array`. Implementors of `List` can remove the `Array` impl and move its methods into the `List` impl (with only a couple tweaks).

```rust
// BEFORE
impl Array for Foo {
  fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */}
  fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */}
  fn len(&self) -> usize {/* ... */}
  fn is_empty(&self) -> bool {/* ... */}
  fn iter(&self) -> ArrayIter {/* ... */}
  fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */}
  fn clone_dynamic(&self) -> DynamicArray {/* ... */}
}

impl List for Foo {
  fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */}
  fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */}
  fn push(&mut self, value: Box<dyn Reflect>) {/* ... */}
  fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */}
  fn clone_dynamic(&self) -> DynamicList {/* ... */}
}

// AFTER
impl List for Foo {
  fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */}
  fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */}
  fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */}
  fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */}
  fn push(&mut self, value: Box<dyn Reflect>) {/* ... */}
  fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */}
  fn len(&self) -> usize {/* ... */}
  fn is_empty(&self) -> bool {/* ... */}
  fn iter(&self) -> ListIter {/* ... */}
  fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */}
  fn clone_dynamic(&self) -> DynamicList {/* ... */}
}
```

Some other small tweaks that will need to be made include:
- Use `ListIter` for `List::iter` instead of `ArrayIter` (the return type from `Array::iter`)
- Replace `array_hash` with `list_hash` in `Reflect::reflect_hash` for implementors of `List`
This commit is contained in:
Gino Valente 2023-02-13 21:07:53 +00:00
parent d7d983be60
commit 724b36289c
5 changed files with 137 additions and 84 deletions

View file

@ -21,19 +21,25 @@ use std::{
pub trait Array: Reflect {
/// Returns a reference to the element at `index`, or `None` if out of bounds.
fn get(&self, index: usize) -> Option<&dyn Reflect>;
/// Returns a mutable reference to the element at `index`, or `None` if out of bounds.
fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>;
/// Returns the number of elements in the collection.
/// Returns the number of elements in the array.
fn len(&self) -> usize;
/// Returns `true` if the collection contains no elements.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns an iterator over the collection.
/// Returns an iterator over the array.
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>>;
/// Clones the list, producing a [`DynamicArray`].
fn clone_dynamic(&self) -> DynamicArray {
DynamicArray {
name: self.type_name().to_string(),

View file

@ -3,11 +3,11 @@ use std::any::Any;
use crate::utility::GenericTypeInfoCell;
use crate::{
Array, ArrayIter, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Reflect,
ReflectFromPtr, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeRegistration, Typed,
FromReflect, FromType, GetTypeRegistration, List, ListInfo, ListIter, Reflect, ReflectFromPtr,
ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeRegistration, Typed,
};
impl<T: smallvec::Array + Send + Sync + 'static> Array for SmallVec<T>
impl<T: smallvec::Array + Send + Sync + 'static> List for SmallVec<T>
where
T::Item: FromReflect,
{
@ -27,25 +27,6 @@ where
}
}
fn len(&self) -> usize {
<SmallVec<T>>::len(self)
}
fn iter(&self) -> ArrayIter {
ArrayIter::new(self)
}
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>
where
T::Item: FromReflect,
{
fn insert(&mut self, index: usize, value: Box<dyn Reflect>) {
let value = value.take::<T::Item>().unwrap_or_else(|value| {
<T as smallvec::Array>::Item::from_reflect(&*value).unwrap_or_else(|| {
@ -77,6 +58,20 @@ where
fn pop(&mut self) -> Option<Box<dyn Reflect>> {
self.pop().map(|value| Box::new(value) as Box<dyn Reflect>)
}
fn len(&self) -> usize {
<SmallVec<T>>::len(self)
}
fn iter(&self) -> ListIter {
ListIter::new(self)
}
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> Reflect for SmallVec<T>
@ -137,7 +132,7 @@ where
}
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(List::clone_dynamic(self))
Box::new(self.clone_dynamic())
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {

View file

@ -180,7 +180,7 @@ impl_from_reflect_value!(NonZeroI8);
macro_rules! impl_reflect_for_veclike {
($ty:ty, $insert:expr, $remove:expr, $push:expr, $pop:expr, $sub:ty) => {
impl<T: FromReflect> Array for $ty {
impl<T: FromReflect> List for $ty {
#[inline]
fn get(&self, index: usize) -> Option<&dyn Reflect> {
<$sub>::get(self, index).map(|value| value as &dyn Reflect)
@ -191,25 +191,6 @@ macro_rules! impl_reflect_for_veclike {
<$sub>::get_mut(self, index).map(|value| value as &mut dyn Reflect)
}
#[inline]
fn len(&self) -> usize {
<$sub>::len(self)
}
#[inline]
fn iter(&self) -> ArrayIter {
ArrayIter::new(self)
}
#[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 $ty {
fn insert(&mut self, index: usize, value: Box<dyn Reflect>) {
let value = value.take::<T>().unwrap_or_else(|value| {
T::from_reflect(&*value).unwrap_or_else(|| {
@ -239,6 +220,23 @@ macro_rules! impl_reflect_for_veclike {
fn pop(&mut self) -> Option<Box<dyn Reflect>> {
$pop(self).map(|value| Box::new(value) as Box<dyn Reflect>)
}
#[inline]
fn len(&self) -> usize {
<$sub>::len(self)
}
#[inline]
fn iter(&self) -> $crate::ListIter {
$crate::ListIter::new(self)
}
#[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> Reflect for $ty {
@ -296,11 +294,11 @@ macro_rules! impl_reflect_for_veclike {
}
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(List::clone_dynamic(self))
Box::new(self.clone_dynamic())
}
fn reflect_hash(&self) -> Option<u64> {
crate::array_hash(self)
crate::list_hash(self)
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {

View file

@ -394,7 +394,7 @@ mod tests {
list.push(3isize);
list.push(4isize);
list.push(5isize);
foo_patch.insert("c", List::clone_dynamic(&list));
foo_patch.insert("c", list.clone_dynamic());
let mut map = DynamicMap::default();
map.insert(2usize, 3i8);
@ -607,11 +607,11 @@ mod tests {
#[test]
fn dynamic_names() {
let list = Vec::<usize>::new();
let dyn_list = List::clone_dynamic(&list);
let dyn_list = list.clone_dynamic();
assert_eq!(dyn_list.type_name(), std::any::type_name::<Vec<usize>>());
let array = [b'0'; 4];
let dyn_array = Array::clone_dynamic(&array);
let dyn_array = array.clone_dynamic();
assert_eq!(dyn_array.type_name(), std::any::type_name::<[u8; 4]>());
let map = HashMap::<usize, String>::default();

View file

@ -1,16 +1,18 @@
use std::any::{Any, TypeId};
use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher};
use crate::utility::NonGenericTypeInfoCell;
use crate::{
Array, ArrayIter, DynamicArray, DynamicInfo, FromReflect, Reflect, ReflectMut, ReflectOwned,
ReflectRef, TypeInfo, Typed,
DynamicInfo, FromReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, Typed,
};
/// An ordered, mutable list of [Reflect] items. This corresponds to types like [`std::vec::Vec`].
///
/// This is a sub-trait of [`Array`], however as it implements [insertion](List::insert) and [removal](List::remove),
/// it's internal size may change.
/// Unlike the [`Array`](crate::Array) trait, implementors of this type are not expected to
/// maintain a constant length.
/// Methods like [insertion](List::insert) and [removal](List::remove) explicitly allow for their
/// internal size to change.
///
/// This trait expects index 0 to contain the _front_ element.
/// The _back_ element must refer to the element with the largest index.
@ -18,7 +20,13 @@ use crate::{
///
/// [`push`](List::push) and [`pop`](List::pop) have default implementations,
/// however it may be faster to implement them manually.
pub trait List: Reflect + Array {
pub trait List: Reflect {
/// Returns a reference to the element at `index`, or `None` if out of bounds.
fn get(&self, index: usize) -> Option<&dyn Reflect>;
/// Returns a mutable reference to the element at `index`, or `None` if out of bounds.
fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>;
/// Inserts an element at position `index` within the list,
/// shifting all elements after it towards the back of the list.
///
@ -47,6 +55,20 @@ pub trait List: Reflect + Array {
}
}
/// Returns the number of elements in the list.
fn len(&self) -> usize;
/// Returns `true` if the collection contains no elements.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns an iterator over the list.
fn iter(&self) -> ListIter;
/// Drain the elements of this list to get a vector of owned values.
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>>;
/// Clones the list, producing a [`DynamicList`].
fn clone_dynamic(&self) -> DynamicList {
DynamicList {
@ -162,7 +184,7 @@ impl DynamicList {
}
}
impl Array for DynamicList {
impl List for DynamicList {
fn get(&self, index: usize) -> Option<&dyn Reflect> {
self.values.get(index).map(|value| &**value)
}
@ -171,31 +193,6 @@ impl Array for DynamicList {
self.values.get_mut(index).map(|value| &mut **value)
}
fn len(&self) -> usize {
self.values.len()
}
fn iter(&self) -> ArrayIter {
ArrayIter::new(self)
}
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
self.values
}
fn clone_dynamic(&self) -> DynamicArray {
DynamicArray {
name: self.name.clone(),
values: self
.values
.iter()
.map(|value| value.clone_value())
.collect(),
}
}
}
impl List for DynamicList {
fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {
self.values.insert(index, element);
}
@ -212,6 +209,18 @@ impl List for DynamicList {
self.values.pop()
}
fn len(&self) -> usize {
self.values.len()
}
fn iter(&self) -> ListIter {
ListIter::new(self)
}
fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {
self.values
}
fn clone_dynamic(&self) -> DynamicList {
DynamicList {
name: self.name.clone(),
@ -292,12 +301,12 @@ impl Reflect for DynamicList {
#[inline]
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(List::clone_dynamic(self))
Box::new(self.clone_dynamic())
}
#[inline]
fn reflect_hash(&self) -> Option<u64> {
crate::array_hash(self)
list_hash(self)
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
@ -333,6 +342,51 @@ impl IntoIterator for DynamicList {
}
}
/// An iterator over an [`List`].
pub struct ListIter<'a> {
list: &'a dyn List,
index: usize,
}
impl<'a> ListIter<'a> {
/// Creates a new [`ListIter`].
#[inline]
pub const fn new(list: &'a dyn List) -> ListIter {
ListIter { list, index: 0 }
}
}
impl<'a> Iterator for ListIter<'a> {
type Item = &'a dyn Reflect;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let value = self.list.get(self.index);
self.index += 1;
value
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.list.len();
(size, Some(size))
}
}
impl<'a> ExactSizeIterator for ListIter<'a> {}
/// Returns the `u64` hash of the given [list](List).
#[inline]
pub fn list_hash<L: List>(list: &L) -> Option<u64> {
let mut hasher = crate::ReflectHasher::default();
std::any::Any::type_id(list).hash(&mut hasher);
list.len().hash(&mut hasher);
for value in list.iter() {
hasher.write_u64(value.reflect_hash()?);
}
Some(hasher.finish())
}
/// Applies the elements of `b` to the corresponding elements of `a`.
///
/// If the length of `b` is greater than that of `a`, the excess elements of `b`
@ -350,7 +404,7 @@ pub fn list_apply<L: List>(a: &mut L, b: &dyn Reflect) {
v.apply(value);
}
} else {
List::push(a, value.clone_value());
a.push(value.clone_value());
}
}
} else {