macro_rules! assert_matches_custom {
($value:expr, $pattern:pat) => {
match $value {
$pattern(result) => result,
_ => panic!(
"assertion failed: expected `{}` to match `{}`",
stringify!($value),
stringify!($pattern)
),
}
};
}
// In some test I want to do something like:
let some_enum = ...
let result = assert_matches_custom!(some_enum, SomeEnum::Expected);
// Assert more things about 'result', which is of the type contained in 'SomeEnum::Expected', like would be the case when writing the full match expression manually.
I'm running into the following error:
expected one of =>, if, or |, found (
For some added context:
I am aware of the unstable 'assert_matches' feature. I'm not using it because my understanding is it would require me to target nightly, and because it doesn't do what I really want (which is to return the matched value to the caller). The 'if ()' guard pattern is a bit too limited for my use-cases.
Writing this code with the variables replaced one-for-one textually works as expected, so it seems to me there's something about the 'pat' type in Rust macros that is leading the compiler to view the '(' as an unexpected token.
I have tried other types than 'pat' (see Macros By Example - The Rust Reference), but a match expression seems to expect the lexeme after 'match value {' to be a 'pattern'.
Any ideas what might be going wrong, something I'm missing, or how I could achieve my goal in some other way?
Blimey, path does work, I must have not tried that one.
I did attempt the whole pattern as an argument at some point, but then I ran into the problem that result was no longer in scope on the next line. It does work the way you've formatted it, though, so clearly I did something wrong before.
I think I'd favor the version that requires the caller to assign the resulting value, as it makes it more clear that result is in fact in scope after the macro completes.
I don't fully understand your question regarding an enum variant that doesn't have a single tuple value. Is there some situation you have in mind where this wouldn't work?
Probably you wrapped the match expression in another scope. I do that if I want to make sure no binding from my macro call is leaked to the surrounding context, like:
macro_rules! assert_matches_custom {
($value:expr, $pattern:path) => {
{ // extra scope
match $value {
$pattern(result) => result,
_ => panic!(
"assertion failed: expected `{}` to match `{}`",
stringify!($value),
stringify!($pattern)
),
}
}
};
}
Yes, in my playground, you can't ever match the second variant SomeEnum::Unexpected, because it doesn't have exactly one tuple argument:
fn main() {
let some_enum = SomeEnum::Expected("Expected".to_owned());
let result = assert_matches_custom!(some_enum, SomeEnum::Unexpected);
println!("{result}");
}
Neat! I only tried it with (result, _) (the compiler suggestion in the error message) but that only matches exactly two arguments. Unfortunately result @ .. to assign result to the whole tuple instead of only the first value doesn't work, var @ .. is only supported for slice patterns.