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

Allow ignoring enum variants in To/FromVariant #697

Open
Tracked by #958
wyattjsmith1 opened this issue Feb 16, 2021 · 3 comments
Open
Tracked by #958

Allow ignoring enum variants in To/FromVariant #697

wyattjsmith1 opened this issue Feb 16, 2021 · 3 comments
Labels
c: export Component: export (mod export, derive) feature Adds functionality to the library
Milestone

Comments

@wyattjsmith1
Copy link

Note: In this issue, I will discuss both rust's enum variants, and Godot's Variants. To avoid confusion I will use an upper case V for Godot's as it is a class, and lowercase v for rust's enums.

Problem

Currently, we can derive ToVariant: https://docs.rs/gdnative/0.9.3/gdnative/core_types/trait.ToVariant.html. This works well, and for the cases where it isn't perfect, we can add #[variant(...)] attributes to specify how the macro should process certain pieces of data. One group of these attributes is #[variant(skip_to_variant|skip_from_variant|skip)]. As the name implies, this will skip a field when converting to/from a Variant. Currently, this must be placed on a struct's field:

#[derive(ToVariant, FromVariant)]
struct MyStruct {
  converted_field: u8,
  #[variant(skip)]
  skipped_field: u8,
}

One area this could be expanded is to skipping enum variants. For example, the following currently results in a compiler error:

#[derive(ToVariant, FromVariant)]
enum MyEnum {
   VariantA,
  #[variant(skip)]
  VariantB(NonToVariantType),
}

In this example, the variant(skip) is ignored, and because VariantB contains a non-ToVariant field, this fails to compile.

Proposal

Allow #[variant(skip)] (and its siblings) to be placed on enum variants. This does pose the following issues:

  1. What if we try to call from_variant with a skipped variant?: We should consider this a failure and return an Err as if we called from_variant with a Dictionary that isn't a variant.
  2. What if we try to call to_variant with a skipped variant?: There are a couple possibilities:
  • Return Nil or possible empty dict. Benefits are no crashing, disadvantages is that there may be silent bugs.
  • panic. Benefits are that we are bringing the issues up to the developer immediately, disadvantage is that panic is not really rusty when it could be better represented as an Result or something.
  1. Why bother ignoring certain variants?: This admittedly comes from a specific use case I have, but I believe it would be good to expand the use of variant(skip). There may be cases where we need to convert a subset of enum variants to Variants while still retaining the non-To/FromVariant variants. My solution in the short-term will be to add #[variant(skip)] to the fields in my variant that are non-To/FromVariant.
@ghost ghost added feature Adds functionality to the library c: export Component: export (mod export, derive) labels Feb 20, 2021
@Bromeon Bromeon added this to the v0.10.1 milestone Nov 1, 2021
@Bromeon Bromeon modified the milestones: v0.10.1, v0.10.2 Jul 16, 2022
@Bromeon Bromeon modified the milestones: v0.10.2, v0.11.x Oct 1, 2022
@chitoyuu
Copy link
Contributor

chitoyuu commented Jan 9, 2023

Question 2 is an interesting one: originally the ToVariant trait was only meant to be implemented for simple types that are easily converted to Variant representations, which is I assume why it was designed to be infallible. This made features like this hard to support though, and I wonder if it's best to redesign the trait to return Result instead, possibly with a provided panicking method for convenience.

Since this doesn't seem to be a very commonly requested feature, it might be best to leave it until the trait redesigns are done: #869 (comment).

@chitoyuu chitoyuu modified the milestones: v0.11.x, v0.12.0, unplanned Jan 9, 2023
@Bromeon
Copy link
Member

Bromeon commented Jan 15, 2023

Question 2 is an interesting one: originally the ToVariant trait was only meant to be implemented for simple types that are easily converted to Variant representations, which is I assume why it was designed to be infallible. This made features like this hard to support though, and I wonder if it's best to redesign the trait to return Result instead, possibly with a provided panicking method for convenience.

This is an interesting topic that I also came across during GDExtension development. In my case, the type in question was u64. I didn't want to use GDNative's behavior where values are silently cast to signed -- but it would be possible to support conversion from the "valid range" of u64, namely the first half of values, which remain the same value when converted to i64.

Since every other conversion was infallible, I decided for now that it was more type-safe to disallow u64 (and leave casts to explicit user code), and to make ToVariant always succeed if it can be implemented.

This example shows though that there may be other use cases. The trade-off I don't like here is that 99% of ToVariant cases are indeed infallible. Introducing Result would weaken API expressivity and introduce tons of unwrap()s all over the place -- a problem that godot-rust already suffers from.

An alternative might be two separate traits, similar to From and TryFrom, but this also adds a lot of complexity and the orphan rule might not be happy with it, either...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: export Component: export (mod export, derive) feature Adds functionality to the library
Projects
None yet
Development

No branches or pull requests

3 participants