Expand description
Β§π Eval Macro
Eval Macro introduces a new macro type for Rust, blending power and ease of use. Hereβs how
it compares to macro_rules!
and procedural macros:
Proc Macro | Eval Macro | Macro Rules | |
---|---|---|---|
Input | Token Stream | Rust Code | Macro Fragments |
Output | Token Stream | Rust Code | Macro Fragments |
Hygienic | β | β | β |
Advanced transformations | β | β | β |
Easy to define | β | β | β |
Easy to read | β | β | β |
Reusable | β | β | β |
In short, Eval Macros offer procedural macro power with macro_rules!
simplicity. However,
they are not reusable β you cannot export an Eval Macro for use in other crates.
Β§π€© Syntax
Use the eval!
macro to create and run an Eval Macro inline. The content of the macro is
regular Rust code, which will be compiled and executed at build time.
Inside the eval!
block, you can use the output!
macro to emit Rust code. output!
supports
double-brace interpolation, allowing you to embed variables directly into the generated
code.
Example:
use eval_macro::eval;
eval! {
let components = ["X", "Y", "Z", "W"];
for (ix, name) in components.iter().enumerate() {
// === Structs Definitions ===
let dim = ix + 1;
let cons = components[0..dim].join(",");
output! {
enum Position{{dim}} {
{{cons}}
}
}
// === Conversions ===
for ix2 in (dim + 1)..=components.len() {
let source = format!("Position{dim}");
let branches = components[0..dim].iter().map(|comp|
format!("{source}::{comp} => Self::{comp}")
).collect::<Vec<_>>().join(",");
output! {
impl From<{{source}}> for Position{{ix2}} {
fn from(src: {{source}}) -> Self {
match src {
{{branches}}
}
}
}
}
}
}
}
This will generate:
enum Position1 { X }
enum Position2 { X, Y }
enum Position3 { X, Y, Z }
enum Position4 { X, Y, Z, W }
impl From<Position1> for Position2 {
fn from(src: Position1) -> Self {
match src {
Position1::X => Self::X
}
}
}
impl From<Position1> for Position3 {
fn from(src: Position1) -> Self {
match src {
Position1::X => Self::X
}
}
}
impl From<Position1> for Position4 {
fn from(src: Position1) -> Self {
match src {
Position1::X => Self::X
}
}
}
impl From<Position2> for Position3 {
fn from(src: Position2) -> Self {
match src {
Position2::X => Self::X,
Position2::Y => Self::Y
}
}
}
impl From<Position2> for Position4 {
fn from(src: Position2) -> Self {
match src {
Position2::X => Self::X,
Position2::Y => Self::Y
}
}
}
impl From<Position3> for Position4 {
fn from(src: Position3) -> Self {
match src {
Position3::X => Self::X,
Position3::Y => Self::Y,
Position3::Z => Self::Z
}
}
}
Doing this with macro_rules!
or procedural macros would be far more complex!
Β§π How It Works
The content inside eval!
is pasted into the main
function of a temporary Rust project.
This project is compiled and executed at build time, and its stdout
becomes the generated
Rust code. The generated main
function looks something like this:
fn main() {
let mut output_buffer = String::new();
{your_code}
println!("{{output_buffer}}");
}
The output!
macro is essentially a shortcut for writing to output_buffer
using format!
,
so this:
use eval_macro::eval;
eval! {
let components = ["X", "Y", "Z", "W"];
for (ix, name) in components.iter().enumerate() {
let dim = ix + 1;
let cons = components[0..dim].join(",");
output! {
enum Position{{dim}} {
{{cons}}
}
}
}
}
Is equivalent to:
use eval_macro::eval;
eval! {
let components = ["X", "Y", "Z", "W"];
for (ix, name) in components.iter().enumerate() {
let dim = ix + 1;
let cons = components[0..dim].join(",");
write_ln!(output_buffer, "
enum Position{dim} {{
{cons}
}}
");
}
}
And that, in turn, is just shorthand for:
use eval_macro::eval;
eval! {
let components = ["X", "Y", "Z", "W"];
for (ix, name) in components.iter().enumerate() {
let dim = ix + 1;
let cons = components[0..dim].join(",");
output_buffer.push_str(&format!("
enum Position{dim} {{
{cons}
}}
"));
}
}
Β§π Dependencies
Each eval!
block can define its own Cargo dependencies, allowing you to pull in external
crates directly within the macro context. This is done using a special pragma attribute:
#![dependency(...)]
.
use eval_macro::eval;
eval! {
#![dependency(anyhow = "1.0")]
type Result<T> = anyhow::Result<T>;
// ...
}
This flexibility allows eval!
macros to seamlessly leverage third-party crates, without
affecting your projectβs main Cargo.toml
.
Β§β οΈ Troubleshooting
β οΈ Note: Rust IDEs differ in how they handle macro expansion. This macro is tuned for
RustRoverβs
expansion engine.
If your IDE struggles to correctly expand eval!
, you can manually switch to the write_ln!
syntax described above. If you encounter issues, please
open an issue to let us know!