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

Deref and DerefMut for enums #388

Open
BenLeadbetter opened this issue Jul 25, 2024 · 4 comments
Open

Deref and DerefMut for enums #388

BenLeadbetter opened this issue Jul 25, 2024 · 4 comments

Comments

@BenLeadbetter
Copy link

Is there a technical reason not to support Deref and DerefMut for enum input types? Seems to me like this would work pretty idiomatically so long as the different variants all supported a consistent target.

For example:

#[derive(derive_more::Deref)]
enum MyEnum1<'a> {
    Variant1(&'a [u8]),
    Variant2(&'a [u8]),
    Variant3(&'a [u8]),
}

#[derive(derive_more::Deref)]
#[deref(forward)]
enum MyEnum2<'a> {
    Variant1([u8; 4]),
    Variant2(Vec<u8>),
}
@JelteF
Copy link
Owner

JelteF commented Jul 25, 2024

For the cases you describe I agree it should be possible (at least the first one, the second one might be harder). So no technical reason, just no-one implemented it yet.

@russellbanks
Copy link

+1 for this. My use case could easily be derived:

enum Compression {
    Stored(u32),
    Zlib(u32),
    LZMA1(u32),
}

impl Deref for Compression {
    type Target = u32;

    fn deref(&self) -> &Self::Target {
        match self {
            Self::Stored(size) | Self::Zlib(size) | Self::LZMA1(size) => size,
        }
    }
}

impl DerefMut for Compression {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            Self::Stored(size) | Self::Zlib(size) | Self::LZMA1(size) => size,
        }
    }
}

@BenLeadbetter
Copy link
Author

I had a short session where I tried to implement this feature. I came across a few speed bumps which changed my opinion a little.

1. We can only support enums with "unnamed" data fields

We already know one condition is that the data fields across the input enum must be consistent in some way. Additionally, unless I'm missing something, we can't support the "named fields" enum data type. Even if data were consistent, with named fields it's not clear what the Deref::Target type should be. For example.

enum MyEnum {
  Variant1 {
    named_field1: u8,
    named_field2: bool,
  },
  Variant2 {
    named_field1: u8,
    named_field2: bool,
  },
}

These variants with named field data are not proper types, so there is no clear type to return in these cases. I guess we could annotate a named field in each variant with #[enabled] - and this would be the target type / value. My gut feeling is that this would feel a bit too contrived.

So I guess this means that we can only support enums with unnamed fields as data.

2. We can only support unnamed fields with 1 tuple field

Even if we restrict support to enums with unnamed fields style data, there's still an issue if the data contains more than one field. For example:

enum MyEnum {
  Variant1(u8, bool),
  Variant2(u8, bool),
}

What would the Deref::Target type be here? It's tempting to try using tuple: type Target = (u8, bool) however, again because the data in an enum variant is not a proper type, we don't actually have such tuple values to reference and return in our Deref::deref implementation.

So additionally we would need to restrict support for deriving Deref to just enums with unnnamed fields data with precicely one unnamed field.

Summary

So overall deriving Deref / DerefMut for enums would have to be restricted to only enums with consistent unnamed field data, each with precisely one unnamed field. My gut feeling now is that implementing this is not worth the trouble due to the lack of generality. I'm keen to hear what other people think, though.

@JelteF
Copy link
Owner

JelteF commented Aug 29, 2024

@BenLeadbetter I think you're making this more complicated than necessary. We can use the same approach that we use for structs with multiple fields here: Adding a #[deref] attribute to the field that you want to create the deref for. It's shown in the documentation in the first example code block (as the third struct):

// You can specify the field you want to derive `Deref` for.
#[derive(Deref)]
struct CoolVec {
    cool: bool,
    #[deref]
    vec: Vec<i32>,
}

So your examples with enums should then look like this, if you want to to create a Deref derive for u8:

#[derive(Deref)]
enum MyEnum {
  Variant1 {
    #[deref]
    named_field1: u8,
    named_field2: bool,
  },
  Variant2 {
    #[deref]
    named_field1: u8,
    named_field2: bool,
  },
}

enum MyEnum {
  Variant1(#[deref] u8, bool),
  Variant2(#[deref] u8, bool),
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants