Skip to content
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

Reflect derive with generic trait bounds #12987

Closed
Veritius opened this issue Apr 15, 2024 · 2 comments
Closed

Reflect derive with generic trait bounds #12987

Veritius opened this issue Apr 15, 2024 · 2 comments
Labels
A-Reflection Runtime information about types C-Enhancement A new feature

Comments

@Veritius
Copy link

What problem does this solve or what need does it fill?

For a simple struct with no generic trait bounds, like so, the reflect derive works.

#[derive(Reflect)]
struct MyStruct;

For a struct with one or more generic trait bounds, like so, the reflect derive does not work.

#[derive(Reflect)]
struct MyStruct<T>(PhantomData<T>);

This is because T isn't bound to be Reflect, which makes sense.
Adding a Reflect bound to T would fix this use case.

However, if T may or may not be Reflect, the overall struct cannot derive Reflect.
Normally this could be solved with impl<T: Reflect> Reflect for MyStruct<T>, except that Reflect is a very dangerous and unsafe to implement trait: #[derive(Reflect)] is the "correct" way to implement Reflect.

There should be some way to derive Reflect with additional bounds.

What solution would you like?

Something like an attribute:

#[derive(Reflect)]
#[reflect(where = "T") // Only implements Reflect when `T` is reflect.
struct MyStruct<T>(PhantomData<T>);

This could be used multiple times.

#[derive(Reflect)])
#[reflect(where = "T", where = "Z")]
struct MyStruct<T, Z>(PhantomData<(T, Z)>);
@Veritius Veritius added C-Enhancement A new feature S-Needs-Triage This issue needs to be labelled labels Apr 15, 2024
@MrGVSV
Copy link
Member

MrGVSV commented Apr 16, 2024

or a struct with one or more generic trait bounds, like so, the reflect derive does not work.

#[derive(Reflect)]
struct MyStruct<T>(PhantomData<T>);

The derive automatically adds the required bounds based on field usage. So, for example, the following:

#[derive(Reflect)]
struct MyStruct<T: Debug>(PhantomData<T>);

Would generate an impl like:

impl<T: Debug> Reflect for MyStruct<T>
where 
  // Required `Self` bounds
  Self: Any + Send + Sync,
  // Field bounds
  PhantomData<T>: FromReflect + Send + Sync,
  // Generic type parameter bounds
  T: TypePath

And note that PhantomData doesn't currently implement Reflect (see #5145), so the above wouldn't compile. Instead, you'd need to do:

#[derive(Reflect)]
struct MyStruct<T: Debug>(#[reflect(ignore)] PhantomData<T>);

Something like an attribute:

#[derive(Reflect)]
#[reflect(where = "T") // Only implements Reflect when `T` is reflect.
struct MyStruct<T>(PhantomData<T>);

There is actually an attribute for this as of 0.13 I believe (might've been 0.12):

#[derive(Reflect)]
#[reflect(where T: FromReflect + Clone, U: Debug)
struct Foo<T, U> {
  t: T,
  #[reflect(ignore)]
  u: U
}

In this case, the derive macro will not add any additional bounds (well, except the bare minimum to make reflection work). So in the above example we have to ignore u since our custom bound doesn't add FromReflect as a trait bound (FromReflect is used sine we didn't specify #[reflect(from_reflect = false)]).

Hopefully that helps!

@MrGVSV MrGVSV added A-Reflection Runtime information about types and removed S-Needs-Triage This issue needs to be labelled labels Apr 16, 2024
@Veritius
Copy link
Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Reflection Runtime information about types C-Enhancement A new feature
Projects
None yet
Development

No branches or pull requests

2 participants