Macro Magic: Building a Custom Assert Macro in Rust
Macros in Rust offer powerful metaprogramming capabilities, allowing you to generate code at compile time. This challenge will guide you through creating a function-like macro that behaves similarly to Rust's built-in assert_eq! macro, but with a custom message. This exercise will deepen your understanding of macro syntax, expression parsing, and code generation.
Problem Description
Your task is to create a Rust macro named my_assert_eq that takes two expressions as arguments. This macro should:
- Compare the two expressions for equality.
- If the expressions are not equal, it should panic with a formatted error message that includes the values of both expressions and the source code representation of the expressions themselves.
- If the expressions are equal, it should do nothing (just like
assert_eq!).
This is useful for writing more informative assertions in your tests or debugging code, especially when the default error messages might not be sufficient.
Examples
Example 1:
// Inside a function or test
let x = 5;
let y = 5;
my_assert_eq!(x, y);
Output: (No output or panic, as x and y are equal)
Explanation: The macro compares x and y. Since they are equal, the macro completes successfully without any side effects.
Example 2:
// Inside a function or test
let a = 10;
let b = 20;
my_assert_eq!(a, b);
Expected Panic Output:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `10`,
right: `20`'
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Explanation: The macro compares a and b. Since they are not equal, it panics. The panic message includes the values of a (10) and b (20), along with the original expression comparison (left == right).
Example 3:
// Inside a function or test
struct Point { x: i32, y: i32 }
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1, y: 3 };
my_assert_eq!(p1.y, p2.y); // This will panic
Expected Panic Output:
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `2`,
right: `3`'
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Explanation: The macro compares the y fields of p1 and p2. As they are not equal, it panics with the values of p1.y (2) and p2.y (3).
Constraints
- The macro must be a function-like macro, meaning it should be invoked using
macro_name!(...). - The macro should accept exactly two arguments, both of which are Rust expressions.
- The expressions passed to the macro must implement the
PartialEqtrait. - The macro should not require any specific traits beyond
PartialEqfor the types of the expressions being compared. - The generated code should leverage standard Rust panic mechanisms.
Notes
- Recall how to define macros in Rust using
macro_rules!. - Consider how to capture the string representations of the input expressions for display in the panic message.
- Think about how to ensure the macro correctly handles different types of expressions, as long as they are comparable with
==. - The
panic!macro is your friend for generating the error message. You can use string formatting withinpanic!. - The
stringify!macro can be helpful in capturing the source code of an expression.