How to get enum's structure and call it's method but not enum's one?

So I have 2 structures and enum that unites them:

struct A {}
struct B {}
enum Symbol {
    SymbolA(A),
    SymbolB(B)
}

and I want to implement a method for structs A and B:

trait say {
    fn say(&self);
}
impl say for A {
    fn say(&self) {
        print!("it's symbol A");
    }
}
impl say for B {
    fn say(&self) {
        print!("it's symbol B");
    }
}

but I store values in a Vector of Symbol:

let vec: Vec<Symbol> = Vec::new()
vec.push(Symbol::SymbolA(A{}));
vec.push(Symbol::SymbolB(B{}));
vec[0].say()

compiler says there is no method say in Symbol. So I implemented it:

impl say for Symbol {
    fn say(&self) {
        print!("Random symbol");
    }
}

but then, the output of

vec[0].say()

is Random symbol but not it's symbol A as I expected.
So how do I call the method say of A? Maybe I shouldn't use Vector of Symbol? But then how do I place A and B in the same vector? Well, I could create two vectors, one for A and one for B, but it would be hard to iterate through them in correct order

match

Well, it is definitely solves issue, thanks, but I feel like I should use rust's Trait. I mean, why then do I need impl TraitName for StructName instead of impl StructName since I either have a variable of the specific struct type so I can call it directly (like this variable.method() ) or I don't know the type of variable, but then can't call it's method (until I use a match, but then I can just call random func, it doesn't have to be implemented in my object)

A trait is only useful if you are going to write generic functions that accept any of your types — that is, functions that will accept not just &Symbol but also an &A or &B that is not wrapped in Symbol. If you are not going to need any such functions, then there is very little advantage to defining a trait.

1 Like

If you want the enum itself to implement that trait, you can implement it yourself, and then do the match and call the functions in each item. Or, you can let the enum_dispatch crate do all that for you.

2 Likes

And I can unwrap A and B out of Symbol using match, right? But since function accepts both A and B, I don't have to write the function call for every sub-struct of the enum Symbol? Or I do?

Yes, but only in a function that accepts Symbol specifically, not in a generic function using a trait.

I'm not sure what you mean here.

If you want a trait, you have to write the appropriate fn say for every type that implements the trait. Once you have written the trait, generic functions need only call the trait function to handle every type implementing the trait.

Does that answer your question?

I mean, unwrapping A and B out of Symbol looks like this, right?

fn somefunc(symbol: Symbol) {
    match symbol {
        Symbol::A(struct_var) => struct_var.say(),
        Symbol::B(struct_var) => struct_var.say()
    }
}

But is there any way to not to write Symbol::A(struct_var) => struct_var.say(), for every item in enum?

Unfortunately, there is not yet anything built into the language for that. You can write a macro that generates the identical match arms:

macro_rules! match_say {
    ($matched:expr, [$( $variant:ident ),*]) => {
        match $matched {
            $( Symbol::$variant(x) => x.say(), )*
        }
    }
}

fn somefunc(symbol: Symbol) {
    match_say!(symbol, [A, B]);
}

but that's not particularly convenient so it's only really worth it for large enums.

1 Like

Ok, got it, thank you for your help.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.