Best way to implement enum variant conversion?


#1

Because RFC#1450 isn’t implemented yet, I’ve defined an enum of tuple structs like this:

pub struct AttributeAction { attrs: …, props: …, x: …, y: … );
pub struct DirectoryAction { attrs: …, props: …, z: … );

enum Action {
Attribute(AttributeAction),
Directory(DirectoryAction),

};

…since doing so allows me to create impls per-enum variant effectively.

It also gives me a general container type so that I can not only have a list of these different types of actions, but can also match on the enum to get at the underlying type object.

So far, this has worked out fairly well. However, I’m now coming to the part where I’m trying to fill out my implementation and I’ve started doing the impls for traits such as std::convert::From.

Implementing From<AttributeAction> for Action, etc. was trivial and works just as I expect.

However, if I’m not mistaken, there’s no (stable) way for me to impl From<Action> for AttributeAction currently – for that, I would need TryFrom.

Am I mistaken? Have I understand the current situation correctly?


#2

If you wanted to convert an Action to an AttributeAction that way then I think you’re right and would need something like TryFrom.

Is this something you really need though?

Is there a problem implementing your own trait or function on Action to attempt to unpack the inner value? Or do you want to be able to write methods that take a TryInto<AttributeAction> and pass an Action or some other type your crate doesn’t own into it?


#3

Well, I can trivially implement a macro to do what I need to (and have), but this was really just about being able to reduce the amount of boilerplate consumers need to access the underlying struct inside the tuple struct.

It would have been nice to be able to do:

let dir: DirectoryAction = a.into();

…instead of something like:

let dir = match a {
    Action::Directory(v) => v,
    _ => unreachable!(),
}

Since if I’m not mistaken, the latter means I have a runtime-based check instead of a compile time-based check?

Well, as I said above, it’s trivial to implement a function or macro to do the unpacking, it just would have been nice to be able to use the native system to do it.

As for implementing my own trait, I hadn’t considered simply using the same logic as what the unstable trait does in the standard library. I had assumed (perhaps wrongly) that if TryFrom wasn’t stable yet, there must be a good reason.

However, looking at the implementation, I can see it’s fairly trivial, and the arguments in the related RFC make it clear that it’s really more about concerns of mixture with already-existing fallible traits.

I will consider that route instead, thanks for suggesting it.

Nothing that complicated; I’ve intentionally avoided scenarios like that.

The intention is to treat the top-level “enum Action” as the container type (for vecs, etc.) and whenever common functionality offered by all variants is used (similar to a trait).

Whenever a consumer needs access to variant-specific functionality, then they are expected to extract the expected variant (via match, a macro, or some other mechanism) and use it directly.

This allows consumers to work in static types instead of using trait objects, and avoids the associated overhead of them as far as I understand.

I could have chosen to make “enum Action” a Trait instead, but then consumers would have to always ensure that they always handled the objects using std::Any so that they could downcast to the expected variant.


#4

Ah sorry, I understand your motivation for wanting enum variants as types, that’ll be a great feature. My question was purely about a std TryFrom trait. I’ve gone and copy+pasta’d that trait before, its implementation is really simple like you say.

The rest makes sense, a trait or macro for extracting the variant value sounds like the way to go and save a bit of match boilerplate. If the variant values are public then a consumer could choose to use their own match if they really feel the need.


#5

No really, that’s ok – I hadn’t even considered simply implementing my own version of that trait, and it seems like that best path available to me at the moment. I really appreciate it, as it’s something simple I overlooked.

Yes, they are public (intentionally), thanks again, and yes, they could use their own match. I’m just trying to be friendly to consumers of the interface as much as possible and as idiomatically as possible.