0

In C, you can define macros using #define to have something globally replaced by the C preprocessor. How can I achieve the same plain-text replacement in rust?

My goal is to define macros that simplifies Rust syntax and reduce boilerplate, such as replacing !!. with .unwrap(). to simulate the "non-null assertion operator" syntax in Kotlin, Swift, or TypeScript, and replacing (\w+)\?\? with Option<$1> to simplify optional type syntax.

For example, the following struct

struct ReturnPath {
    name: Option<String>,
    file_type: Option<String>,
    mtime: Option<i64>
}

would be much shorter in the processed syntax:

struct ReturnPath {
    name: String??,
    file_type: String??,
    mtime: i64??
}
Hykilpikonna
  • 1,749
  • 2
  • 15
  • 32
  • First of all, your `Option` example is not plain test replacement like the C preprocessor. Secondly, there is no easy way to achieve this. You might be able to use a procedural macro, but please don't. The easiest way to achieve this would be to just apply a bunch of regex replaces on the source file and generate a `.rs` file from that. But again, you should probably just stick with normal Rust. – PitaJ Feb 17 '23 at 18:03
  • 2
    What you _really_ should do is create an RFC to add your syntax suggestions to Rust – cadolphs Feb 17 '23 at 18:17
  • 2
    @tadman Pretty sure attribute macros can only annotate items that are syntactically valid Rust and derive macros cannot modify the struct definition itself either. – kmdreko Feb 17 '23 at 19:13
  • @kmdreko Good point, though it is always evolving in the direction of being more open-ended. – tadman Feb 17 '23 at 20:55
  • I mean nothing stops you from running the C pre-processor on .rs files, if that lets you achieve the syntax you want or is a thing you should do is questionable though. – cafce25 Feb 17 '23 at 21:13

1 Answers1

1

How can I achieve the same plain-text replacement in rust?

You can't do this without a DSL macro, essentially.

C macros are token-replacement and happen before the syntax-checking step -- Rust macros are more akin to Lisp macros, which happen during the parsing step. See: https://softwareengineering.stackexchange.com/a/28387

replacing (\w+)?? with Option<$1> to simplify optional type syntax.

This also won't do what you want it to do in more complicated scenarios, such as if you have Result<T, R>?? or Vec<&'static str>?? or something like that. This is because Rust's grammar is more powerful than regexes -- it is mostly context-free with some context-sensitive parts (link).

What you're probably looking for is some sort of declarative and syntactical replacement from ($t:ty) \? \? => Option<$t>.

In addition, this regex can also probably match on non-types like a?? if a: Option<Option<T>>.

If you wanted to create a special syntax for this, you'd probably do something like:

macro_rules! shorthand_struct_fields {

  /* rows of idents to types and then adjust struct definition but with T?? substituted for Option<T> */

}

struct Foo {
shorthand_struct_fields! {
  x: i32??,
}
}

I'm not sure if this is such a good idea, but it you do want to create this large macro, you would be best suited by writing a declarative macros with sub-macros that use @ rules, like so: http://danielkeep.github.io/tlborm/book/pat-internal-rules.html

CinchBlue
  • 6,046
  • 1
  • 27
  • 58