|
| 1 | +# `macro_metavar_expr_concat` |
| 2 | + |
| 3 | +The tracking issue for this feature is: [#124225] |
| 4 | + |
| 5 | +------------------------ |
| 6 | + |
| 7 | + |
| 8 | +`#![feature(macro_metavar_expr_concat)]` provides a more powerful alternative to [`concat_idents!`]. |
| 9 | + |
| 10 | +> This feature is not to be confused with [`macro_metavar_expr`] or [`concat_ident`]. |
| 11 | +
|
| 12 | +> This is an experimental feature; it and its syntax will require a RFC before stabilization. |
| 13 | +
|
| 14 | + |
| 15 | +### Overview |
| 16 | + |
| 17 | +`macro_rules!` macros cannot create new identifiers and use them in ident positions. |
| 18 | +A common use case is the need to create new structs or functions. The following cannot be done[^1]: |
| 19 | + |
| 20 | +```rust,compile_fail |
| 21 | +macro_rules! create_some_structs { |
| 22 | + ($name:ident) => { |
| 23 | + // Invalid syntax |
| 24 | + struct First_$name; |
| 25 | + // Also invalid syntax |
| 26 | + struct Second_($name); |
| 27 | + // Macros are not allowed in this position |
| 28 | + // (This restriction is what makes `concat_idents!` useless) |
| 29 | + struct concat_ident!(Third_, $name); |
| 30 | + } |
| 31 | +} |
| 32 | +# create_some_structs!(Thing); |
| 33 | +``` |
| 34 | + |
| 35 | +`#![feature(macro_metavar_expr_concat)]` provides the `concat` metavariable to concatenate idents in ident position: |
| 36 | + |
| 37 | +```rust |
| 38 | +#![feature(macro_metavar_expr_concat)] |
| 39 | +# #![allow(non_camel_case_types, dead_code)] |
| 40 | + |
| 41 | +macro_rules! create_some_structs { |
| 42 | + ($name:ident) => { |
| 43 | + struct ${ concat(First_, $name) }; |
| 44 | + struct ${ concat(Second_, $name) }; |
| 45 | + struct ${ concat(Third_, $name) }; |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +create_some_structs!(Thing); |
| 50 | +``` |
| 51 | + |
| 52 | +This macro invocation expands to: |
| 53 | + |
| 54 | +```rust |
| 55 | +# #![allow(non_camel_case_types, dead_code)] |
| 56 | +struct First_Thing; |
| 57 | +struct Second_Thing; |
| 58 | +struct Third_Thing; |
| 59 | +``` |
| 60 | + |
| 61 | +### Syntax |
| 62 | + |
| 63 | +This feature builds upon the metavariable expression syntax `${ .. }` as specified in [RFC 3086] ([`macro_metavar_expr`]). |
| 64 | + `concat` is available like `${ concat(items) }`, where `items` is a comma separated sequence of idents and/or string literals. |
| 65 | + |
| 66 | +### Examples |
| 67 | + |
| 68 | +#### Create a function or method with a concatenated name |
| 69 | + |
| 70 | +```rust |
| 71 | +#![feature(macro_metavar_expr_concat)] |
| 72 | +# #![allow(non_camel_case_types, dead_code)] |
| 73 | + |
| 74 | +macro_rules! make_getter { |
| 75 | + ($name:ident, $field: ident, $ret:ty) => { |
| 76 | + impl $name { |
| 77 | + pub fn ${ concat(get_, $field) }(&self) -> &$ret { |
| 78 | + &self.$field |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +pub struct Thing { |
| 85 | + description: String, |
| 86 | +} |
| 87 | + |
| 88 | +make_getter!(Thing, description, String); |
| 89 | +``` |
| 90 | + |
| 91 | +This expands to: |
| 92 | + |
| 93 | +```rust |
| 94 | +pub struct Thing { |
| 95 | + description: String, |
| 96 | +} |
| 97 | + |
| 98 | +impl Thing { |
| 99 | + pub fn get_description(&self) -> &String { |
| 100 | + &self.description |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +#### Create names for macro generated tests |
| 106 | + |
| 107 | +```rust |
| 108 | +#![feature(macro_metavar_expr_concat)] |
| 109 | + |
| 110 | +macro_rules! test_math { |
| 111 | + ($integer:ident) => { |
| 112 | + #[test] |
| 113 | + fn ${ concat(test_, $integer, _, addition) } () { |
| 114 | + let a: $integer = 73; |
| 115 | + let b: $integer = 42; |
| 116 | + assert_eq!(a + b, 115) |
| 117 | + } |
| 118 | + |
| 119 | + #[test] |
| 120 | + fn ${ concat(test_, $integer, _, subtraction) } () { |
| 121 | + let a: $integer = 73; |
| 122 | + let b: $integer = 42; |
| 123 | + assert_eq!(a - b, 31) |
| 124 | + } |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +test_math!(i32); |
| 129 | +test_math!(u64); |
| 130 | +test_math!(u128); |
| 131 | +``` |
| 132 | + |
| 133 | +Running this returns the following output: |
| 134 | + |
| 135 | +```text |
| 136 | +running 6 tests |
| 137 | +test test_i32_subtraction ... ok |
| 138 | +test test_i32_addition ... ok |
| 139 | +test test_u128_addition ... ok |
| 140 | +test test_u128_subtraction ... ok |
| 141 | +test test_u64_addition ... ok |
| 142 | +test test_u64_subtraction ... ok |
| 143 | +
|
| 144 | +test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s |
| 145 | +``` |
| 146 | + |
| 147 | +[^1]: An alternative is the [`paste`] crate. |
| 148 | + |
| 149 | +[`paste`]: https://crates.io/crates/paste |
| 150 | +[RFC 3086]: https://rust-lang.github.io/rfcs/3086-macro-metavar-expr.html |
| 151 | +[`concat_idents!`]: https://doc.rust-lang.org/nightly/std/macro.concat_idents.html |
| 152 | +[`macro_metavar_expr`]: ./macro-metavar-expr.md |
| 153 | +[`concat_ident`]: ./concat-ident.md |
| 154 | +[#124225]: https://github.com/rust-lang/rust/issues/124225 |
0 commit comments