After the inclusion of the matches macro into std, there are still a lot of similar use cases that it can't be used with:
Those that require extracting a value like this:
match param {
ParamSelector::Int(_, TrackParam::Ext(name)) => Some(name),
_ => None,
}
These patterns occur frequently (or variations with if-let).
There is currently no concise one-line way to write this.
So I adapted the matches macro to work for these use cases:
#[macro_export]
macro_rules! match_map {
($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )? => $ret:expr) => {
match $expression {
$( $pattern )|+ $( if $guard )? => Some($ret),
_ => None
}
}
}
enum E { A(i32), B(i32), C }
fn main() {
assert_eq!(match_map!(E::A(21), E::A(x) | E::B(x) => x * 2), Some(42));
}
I use this macro as often as matches and think it would be useful to have it in std.
Compared to using the more verbose match/if-let way, this macro also has the benefit of readability, when the value is post-processed using .map(..)/.and_then(..)/.ok_or_else(..) or similar Option methods.
Compare:
(match param {
TrackParam::Ext(name) => Some(name),
_ => None,
}).and_then(|name| func(name))
and:
match_map!(param, TrackParam::Ext(name) => name).and_then(|name| func(name))
Maybe put this in a crate for a while and see if it gets picked up.
I鈥檓 generally in favor. I tend to create similar macros for tests.
I also create derive macros (Decompose) to produce this as an inherent method.
We might pursue the field puns color with a proc macro that extracts variant's fields based upon names?
enum Foo {
A(..), // Ignore all tuple variants because .0, etc. rarely turn out consistent enough
B { x: u32, y: u32 },
C { x: u16, y: u32 },
}
fn foo(f: Foo) {
variant_field! { f; f.x } // Error, x has inconsistent types
variant_field! { f; u32::from(f.x) } // Ok, the code inside resolves in all cases
variant_field! { f; f.y } // Ok, all y have consistent types
}
An easier syntax might be
variant_field! { f { x, ..}; bar(x) } // Error, x has inconsistent types
It'd desugar as applying this binding {x, ..} to all variants with those fields and returning Some(..), or _ => None.
Another take on this: bikeshed!(AAA => BBB) => if AAA { Some(BBB) } else { None}. Then you'd have let opt1 = bikeshed!( let Foo::B { x, .. } = foo => x ) but also other things like let opt2 = bikeshed!(x > 3 => 4);.
Of course, I've also pondered before changing the implicit else { () } to else { SomeTrait::method() } so that this would work with just normal if. Shouldn't even cause inference problems, since you need to be producing a () from the if anyway today...
Most helpful comment
I鈥檓 generally in favor. I tend to create similar macros for tests.
I also create derive macros (
Decompose) to produce this as an inherent method.