-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reflection: try_apply function #6770
Conversation
ConsiderationsTo give further details on the alternatives, here's a quick summary of some of the considerations to be made:
|
for (i, value) in struct_value.iter_fields().enumerate() { | ||
let name = struct_value.name_at(i).unwrap(); | ||
if let Some(v) = #bevy_reflect_path::Struct::field_mut(self, name) { | ||
if let Err(e) = v.try_apply(value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be simplified with the ?
operator? Same elsewhere in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes probably. I completely forgot about that syntax. Thanks!
I think the current implementation of taking in |
It seems the build is partly failing. It runs fine on my machine (Mac m1). CI gives this error: error[E0046]: not all trait items implemented, missing: `try_apply` impl Reflect for &'static Path {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `try_apply` in implementation I have double checked every file but can't find the implementation for &'static Path anywhere so I assume it gets derived automatically. Also: these are the tests I have used: Testsmod test {
use bevy::reflect::{DynamicEnum, DynamicList, DynamicVariant, Reflect};
#[test]
fn test_try_apply_values() {
// Strings
let mut s1 = String::from("Hello");
let s2 = String::from("World");
// Ok
let result = s1.try_apply(s2.as_reflect());
assert!(result.is_ok());
// Err
let result = s1.try_apply(0.as_reflect());
assert!(result.is_err());
// Floats
let mut f1 = 10.0;
let f2 = 15.0;
// Ok
let result = f1.try_apply(f2.as_reflect());
assert!(result.is_ok());
assert_eq!(f1, 15.0);
// Err
let result = f1.try_apply(String::from("hi").as_reflect());
assert!(result.is_err());
// Integers
let mut i1 = 1;
let i2 = 2;
// Ok
let result = i1.try_apply(i2.as_reflect());
assert!(result.is_ok());
assert_eq!(i1, 2);
// Err
let result = i1.try_apply(String::from("ha").as_reflect());
assert!(result.is_err());
// Booleans
let mut b1 = true;
let b2 = false;
// Ok
let result = b1.try_apply(b2.as_reflect());
assert!(result.is_ok());
assert_eq!(b1, false);
// Err
let result = b1.try_apply(String::from("ho").as_reflect());
assert!(result.is_err());
}
#[test]
fn test_try_apply_lists() {
// Lists
let mut l1 = DynamicList::default();
l1.push("hi".to_string());
let mut l2 = DynamicList::default();
l2.push("da".to_string());
l2.push(0);
let mut l3 = DynamicList::default();
l3.push(0);
l3.push("ba".to_string());
// Ok
let result = l1.try_apply(l2.as_reflect());
assert!(result.is_ok());
// Err
let result = l1.try_apply(l3.as_reflect());
assert!(result.is_err());
// Arrays
let mut a1 = [0, 1, 2];
let a2 = [2, 1, 0];
let a3 = [true, false, true];
// Ok
let result = a1.try_apply(a2.as_reflect());
assert!(result.is_ok());
// Err
let result = a1.try_apply(a3.as_reflect());
assert!(result.is_err());
// Vectors
let mut v1 = vec![0, 1, 2];
let v2 = vec![2, 1, 0];
let v3 = vec![true, false, true];
// Ok
let result = v1.try_apply(v2.as_reflect());
assert!(result.is_ok());
assert_eq!(v1, v2);
// Err
let result = v1.try_apply(v3.as_reflect());
assert!(result.is_err());
}
#[test]
fn test_try_apply_structs() {
#[derive(Reflect, Debug)]
struct Person {
name: String,
age: i32,
}
#[derive(Reflect, Debug)]
struct Alien {
name: String,
}
#[derive(Reflect, Debug)]
struct Weird {
name: i32,
}
#[derive(Reflect, Debug)]
struct NestedI32 {
name: String,
values: Vec<i32>,
}
#[derive(Reflect, Debug)]
struct NestedString {
name: String,
values: Vec<String>,
}
let mut person1 = Person {
name: "Hello".to_string(),
age: 21,
};
let person2 = Person {
name: "World".to_string(),
age: 18,
};
let alien = Alien {
name: "X Æ A-12".to_string(),
};
let weird = Weird { name: 1 };
let mut nested1 = NestedI32 {
name: String::from("Nested Struct"),
values: vec![0, 1, 2, 3],
};
let nested2 = NestedI32 {
name: String::from("Nested Struct 2"),
values: vec![3, 2, 1, 0],
};
let nested3 = NestedString {
name: String::from("Nested String Struct"),
values: vec![String::from("hi")],
};
// Ok
let result = person1.try_apply(person2.as_reflect());
assert!(result.is_ok());
// Ok
let result = person1.try_apply(alien.as_reflect());
assert!(result.is_ok());
// Err
let result = person1.try_apply(weird.as_reflect());
assert!(result.is_err());
let result = person1.try_apply(0.as_reflect());
assert!(result.is_err());
// Ok
let result = nested1.try_apply(nested2.as_reflect());
assert!(result.is_ok());
// Err
let result = nested1.try_apply(nested3.as_reflect());
assert!(result.is_err());
}
#[test]
fn test_try_apply_enums() {
let mut value = Some(123);
let mut dyn_enum =
DynamicEnum::new(Reflect::type_name(&value), "None", DynamicVariant::Unit);
let result = value.try_apply(&dyn_enum);
assert!(result.is_ok());
assert_eq!(None, value);
let result = dyn_enum.try_apply(value.as_reflect());
assert!(result.is_ok());
}
#[test]
fn test_try_apply_tuples() {
let mut t1 = (true, String::from("hi"));
let t2 = (false, String::from("ha"));
let t3 = (true, 0);
let result = t1.try_apply(t2.as_reflect());
// Ok
assert!(result.is_ok());
assert_eq!(t1, (false, String::from("ha")));
// Err
let result = t1.try_apply(t3.as_reflect());
assert!(result.is_err());
}
} |
you're going to rebase with #6755 in that case. |
78f5fbe
to
13c9a9b
Compare
// You can "apply" Reflect implementations on top of other Reflect implementations.
// This will only set fields with the same name, and it will fail if the types don't match.
// You can use this to "patch" your types with new values.
value.apply(&patch);
assert_eq!(value.a, 4); here it only mentions it will |
rebased with bevyengine#6829
85fde81
to
a3cae9a
Compare
a3cae9a
to
6b3e47c
Compare
@feyokorenhof are you still working on this? Totally fine if not, just let me know so I can nominate this for adoption. |
@alice-i-cecile hey. Not any time soon no sorry! If it’s still open when I have some time I would be happy to take a look at it again but by all means put It up for adoption 😃 |
Awesome, thanks for the reply and for the initial draft :) Regardless of what happens I'll make sure you get credited in the release notes when this ships. |
I'd like to pick this up. |
Awesome, thanks @Brezak! |
# Objective Finish the `try_apply` implementation started in #6770 by @feyokorenhof. Supersedes and closes #6770. Closes #6182 ## Solution Add `try_apply` to `Reflect` and implement it in all the places that implement `Reflect`. --- ## Changelog Added `try_apply` to `Reflect`. --------- Co-authored-by: Feyo Korenhof <feyokorenhof@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
Objective
Reflect.apply()
.Solution
I basically copied the existing code for
apply
and altered it to return a new error typeApplyError
.The way it works now is that the error 'bubbles' up all the way up to the caller with an appropriate error so that the user can handle it on their end.
Note: this 'version' of my implementation takes in a
&mut self
so It will leave data partially mutated when an error occurs. It is up to the user to handle the error and, for example, clone the data beforehand.Definition:
Implementation for list:
Changelog
try_apply
to all the types that implementReflect
.ApplyError
inreflect.rs
. (not finished, I half-assed the errors, will fix).reflect.rs
(would like some feedback on how to write this nicely).try_apply
to the examples inutility.rs
andtype_info.rs
.Migration Guide
No breaking changes
Additional information
This is just a draft so that I can get some feedback on the implementation and thought process.
This version of
try_apply
takes in a&mut self
and potentially leaves the value it was called on in a partially mutated state. We've had some discussion on wether it would be preferred to take in:immutable reference
toself
and return aBox<dyn Reflect>
as result.mutable reference
and clone once at the top so that we can return the original untouched data to the user when an error occurs.Box<Self>
and return aBox<dyn Reflect>
. (I tried to implement this but I haven't figured it out quite yet).