15

Is it possible to generate a symbol or identifier in a Rust macro from a string? Or to perform string-like operations on a identifier?

I wanted to generate a method given a symbol, but need to downcase it to obtain the method name.

get!(B);

// should expand to

fn b() -> B {
    // method body
}

It's easy to get close...

macro_rules! get {
    ($kind:ident, $method:ident)
        =>
    {
        fn $method() -> $kind {
           // method body
        }
    }
}

get!(B, b)

But dissatisfying.

ljedrz
  • 20,316
  • 4
  • 69
  • 97
Phil Lord
  • 2,917
  • 1
  • 20
  • 31
  • 2
    Relevant: https://stackoverflow.com/q/23061702/1233251 https://stackoverflow.com/q/27415011/1233251 – E_net4 Sep 05 '18 at 13:22
  • I run into this language limitation all the time. It'd make the code so much more pleasant to have this feature. – Luke Dupin Mar 30 '19 at 21:20

3 Answers3

14

I just wrote a procedural macro (casey) to do this.

#![feature(proc_macro_hygiene)]

use casey::lower;

lower!(B); // would render as `b`

Update

proc_macro_hygiene is stable as of rust 1.45.0, so no longer requires nightly.

stacksonstacks
  • 8,613
  • 6
  • 28
  • 44
  • 1
    Stuff like this puts a big big smile on my face. Thanks to your good work, one of my business logic source files went from 2k+ lines to something like 400 . Can you tell how happy I am? – cyqsimon May 25 '23 at 06:01
8

The previous answers are all correct; standard declarative macros can't do this, and you can drop to procedural macros instead. However, a simpler alternative to procedural macros (especially if, like myself, that's an area of the language you haven't delved into yet) is dtolnay's paste crate.

An example from those docs:

use paste::paste;

paste! {
    // Defines a const called `QRST`.
    const [<Q R S T>]: &str = "success!";
}

fn main() {
    assert_eq!(
        paste! { [<Q R S T>].len() },
        8,
    );
}

Case conversion is also supported, e.g. [<ld_ $reg:lower _expr>]

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Bryan Henry
  • 8,348
  • 3
  • 34
  • 30
3

No, there isn't a macro that can perform this sort of string manipulation on identifiers.

It is possible to create such a macro in the compiler, but it doesn't seem to be a popular need; today only the experimental concat_idents! comes anything close to this (i.e. string-like operations for identifiers).

Your workaround is currently the only available solution.

ljedrz
  • 20,316
  • 4
  • 69
  • 97