Hi There! I would like to find a method to simplify the following code, to avoid that match statement which is a bit ugly. Do you have any suggestions for this simplification?
pub enum TransactionEx {
V1 {
tx: TransactionV1,
},
V2 {
tx: TransactionV2,
}
}
impl Committed for TransactionEx {
fn inputs_committed(&self) -> Vec<Commitment> {
// >>>> **this part looks a bit ugly, any suggestion to simplify this?** <<<<
match self {
TransactionEx::V1{ tx } => tx.inputs_committed(),
TransactionEx::V2{ tx } => tx.inputs_committed(),
}
}
}
impl Committed for TransactionV1 {
fn inputs_committed(&self) -> Vec<Commitment> {
// body...
}
}
impl Committed for TransactionV2 {
fn inputs_committed(&self) -> Vec<Commitment> {
// different body...
}
}
Long story short: no that isn't possible, because the field needs to be extracted regardless of which enum variant is used. Which in turn means a match or one of its syntactic relatives.
// Apply a match body to all the variants (must all be single value tuples)
macro_rules! match_all {
($on:expr, $enum:ident, $with:ident, $body:tt, $($var:ident),*) => {
match $on {
$(
$enum::$var($with) => { $body },
)*
}
}
}
// TransactionEx-specific version, will need updated as variants are added
macro_rules! dispatch_transaction_ex {
($on:expr, $with:ident, $body:tt) => {
match_all!($on, TransactionEx, $with, $body, V1, V2)
}
}
// The macro in action
impl Committed for TransactionEx {
fn inputs_committed(&self) -> Vec<Commitment> {
dispatch_transaction_ex!(self, tx, { tx.inputs_committed() })
}
}
(May need adjusted depending on what exactly you need.)
There's a difference between simplicity of the code and you not liking the use of a match. The trait-based alternative is viable, but more verbose and complex. So the code already is as simple as it can ever be.
As others have pointed out, already, your version is as good as it gets. The whole point of writing the match inside the enum trait implementation is, that you don't have to write the match outside of the enum, anymore. The match has to be written somewhere. Rust doesn't have any language-specific features to help you bridge between the low-level implementation details and the high-level usage for traits + enums except macros (I don't recommend dynamic dispatch and relying on the compiler to optimize it away). That's your task as a programmer.
Also, I wouldn't avoid macros for this particular task. You'll have to write the delegating match expression for every trait method and that will become very repetetive, unless you only have this single trait method to implement. I would keep the macro simpler than what was suggested, tho.
You might be able to omit the curly brackets in this case, but I'm not sure, as I'm not an expert (I just copied the macro from @quinedot and edited it).