Static std::mem::Discriminant<>?

Hi Everyone,

I'm wondering is there is a way to statically initialize / declare a std::mem::Discriminant? And then is there a way to use that value as an arm of a match statement?

As I understand Discriminants, they can be used to compare two enums that have data without comparing the data. e.g.

if std::mem::discriminant(my_enum_1) == std::mem::discriminant(my_enum_2)

But, I want to create a function that takes a std::mem::Discriminant<> as an argument, and then does a match on the enum type within the function body.

I can't pass a reference to the enum itself into the function, because I don't want to borrow it. I also don't want to clone the first enum for performance reasons. The data in the enum is quite large.

I realize, I could create a work-around, that avoids the use of Discriminant<> altogether, by creating a second enum, which has all of the same possible values as the first enum, but no associated data. Then I could match on the first enum (with associated data) and create an instance of the second enum (without data), and then pass that second enum, and match on it inside the function. But that's a lot of typing for something it feels like the language should just have.

Am I missing something, or is there a better Rust pattern I should be using here?

Thank you in advance.

The question is what do you want to achieve? Do you want to know if two instances have the same variant without looking at the data? Why don't you implement PartialEq then and use mem::discriminant to compare it.

Can you elaborate "I can't pass a reference to the enum itself into the function, because I don't want to borrow it."? It's the best and easiest way to do so.

Thank you for your reply.

The question is what do you want to achieve?

Basically I just want a function that has different behavior, depending on the "type" of an enum argument. My enum is a wrapper around a bunch of different structs.

Can you elaborate "I can't pass a reference to the enum itself into the function, because I don't want to borrow it." ?

The problem is that I'm already passing a mutably-borrowed reference to a field of the struct that I have wrapped in the enum. So I can't immutably borrow the whole enum at the same time. I also don't want to rewrite the function to extract the field from the struct inside the function because I feel like it muddies up my internal abstractions too much.

Thank you again.

It is really hard to find out more and more details from prose. Please post your actual code.

Heya, just reiterating the problem in code:

#[derive(Debug)]
pub enum MyEnum {
    Variant1 { field: String },
    Variant2 { field: Vec<u32> },
}

let enum_instance = MyEnum::Variant1 { field: String::from("..") };

// want to do something like this:
fn do_something(discriminant: MyEnumWithoutData) {
    // ..
}

// but don't want to do any of these:
do_something(&enum_instance);
do_something(enum_instance.clone());

// yet cannot do this, because the function inside needs to instantiate a `MyEnum` to compare on the discriminant.
do_something(std::mem::discriminant(enum_instance));

// and this is duplicating code:
#[derive(Clone, Copy, Debug)]
pub enum MyEnumDiscriminants {
    Variant1,
    Variant2,
}

Is it alright if MyEnumDiscriminants is generated by a proc-macro (strum -- docs), so you can just declare MyEnum like this:

use strum_macros::EnumDiscriminants;

#[derive(Debug, EnumDiscriminants)] // generates `MyEnumDiscriminants`
pub enum MyEnum {
    Variant1 { field: String },
    Variant2 { field: Vec<u32> },
}

// then you can do this:
let enum_instance = MyEnum::Variant1 { field: String::from("..") };
let enum_discriminant = MyEnumDiscriminants::from(&enum_instance);

fn do_something(discriminant: MyEnumDiscriminants) {
    // ..
}

I've lived with using strum and strum_macros, not sure if there's a better way.

2 Likes

Azriel91, you are awesome! Translating my English into code is above and beyond. You are truly a wonderful human.

// yet cannot do this, because the function inside needs to instantiate a MyEnum to compare on the discriminant.
do_something(std::mem::discriminant(enum_instance));

Actually I can't do this:

fn do_something(discriminant: &MyEnum, my_vec : &mut Vec<u32>) {
    //..
}

let mut enum_instance = MyEnum::Variant1 { field: String::from("..") };

if let MyEnum::Variant2{field : my_vec} = &mut enum_instance {
    do_something(&enum_instance, my_vec);
}

The borrow checker complains (rightly so) about the immutable borrow on top of the mutable borrow. So what I really want to do is:

fn do_something(discriminant: std::mem::Discriminant<MyEnum>, my_vec : &mut Vec<u32>) {
    //..
}

let mut enum_instance = MyEnum::Variant2 { field: vec!(0) };

let my_discriminant = std::mem::discriminant(&enum_instance);
if let MyEnum::Variant2{field : my_vec} = &mut enum_instance {
    do_something(my_discriminant, my_vec);
}

Then everything is peachy. Until I try to do something useful with the discriminant argument inside do_something(). What I would really love would be something like this:

fn do_something(discriminant: std::mem::Discriminant<MyEnum>, my_vec : &mut Vec<u32>) {

    match discriminant {
        StaticDiscriminant!<MyEnum::Variant1> => //..
        StaticDiscriminant!<MyEnum::Variant2> => //..
    }
}

Your suggestion about the strum_macros really seem to be the closest thing that there is. Thank you very very much for pointing me at them! I wish the language had something lower overhead, but as far as code cleanliness and readability, the macros are great.

Thank you again!

1 Like