Open Boscop opened 4 years ago
Maybe put this in a crate for a while and see if it gets picked up.
I’m 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...
My try_match
crate provides something similar to that. I personally find this quite useful as a shortcut in some situations. Here are some examples:
pub fn field(&self) -> Option<&FieldDef> {
try_match!(Self::Field(field) = self).ok()
}
// Update `first_free` to point to the next vacant entry (if there's one).
// This `unsafe` is safe because all entires in the linked list
// `self.first_free` are supposed to be vacant.
let next_ptr = try_match!(EntryState::Vacant(next_ptr) = old_state)
.unwrap_or_else(|_| unsafe { unreachable_unchecked() });
This macro evaluates to Ok(matched_things)
on successful match and Err(original_value)
otherwise. I haven't found any wild or my usages that use the Err
variant, so returning Option
will probably suffice in practice, though Result
could be useful to augment the unwrap
panic message.
After the inclusion of the
matches
macro intostd
, there are still a lot of similar use cases that it can't be used with: Those that require extracting a value like this:These patterns occur frequently (or variations with
if-let
). There is currently no concise one-line way to write this. So I adapted thematches
macro to work for these use cases:https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=a3df83eee3d96f05233d78d00f7bc801
I use this macro as often as
matches
and think it would be useful to have it instd
.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 similarOption
methods.Compare:
and: