Single Token Macro Sugar (Custom Literals)

Continuing the discussion from Macro Keyword:

If we allowed macros that take exactly 1 token to be called as macro! token then we would effectively have custom literals. This could be as simple as u256!123456789123456789 or s!"custom String literals" or as complex as other programming languages as literals. A token being something matched by the tt macro specifier, but excluding grouping tokens for back-conpat.

This is a rather small change, but it opens the door to some very interesting and useful idioms. (For example, String literals)

6 Likes

Is there a typo for that? Shouldn't it be u256!123456789?

I believe same goes to the other case, r!b"text" for action on array.

Regarding the macro, should it be?

macro_rules! macro {
    $text:expr => {
        ...
    }
}

What if we need stringify! version?

1 Like

Whoops, yes that should. But I don't think that whitespace should matter here.

I think there’s potential syntactical conflict with option C in Bring back item-like macros. If you mean “single token” literally (as in: any token [except parentheses]), then there’s for sure conflict and even with option A and B.

1 Like

If we ever do this, I'd favor a whitelist of token types, since most of the sigil tokens wouldn't make any sense here and the motivation is basically just string and number literals.

4 Likes

You say it opens the door for useful and interesting idioms, but it doesn't seem to me like a new syntax for

pub fn s(t: &'static str) -> String { String::from(t) }

is all that interesting. Why is s!"text" more useful than s("text")? What can you do with a string or an number literal in a macro that you can't do in a function, aside from assigning it funky syntax? I don't have any experience with proc macros, so maybe there's a way to parse a string without just calling a parse function, so if that's doable, I'd like to have my conceptions corrected.

1 Like

The macro can do compile-time manipulation. That said, this is already doable via s!("text"), so the question really is whether or not the group delimiters are worth eliminating (I'm personally against such a superficial change).

1 Like

That sounds like a good idea, I'd also like identifiers, so we can write things like pin_mut! future. But I can do without identifiers. Limiting the scope to actual literals seems fine as a first step.

For a more useful example, we could have precompiled regex literals using something like this.

This is some minor syntactic sugar to make things easier to read and write. By getting rid of a set of parentheses, it becomes significantly easier to read (especially when nested in other expressions). Nothing here will allow Rust to do something new, it will just make things easier to read/write.

1 Like

That's my question: what manipulation can you do on a single token?

:thinking:

With #![feature(stmt_expr_attributes)] you probably can do this:

let lit = #[u256] 123456789123456789123456789123456789;
2 Likes

This would require proc-macros, where as this sugar also support macro_rules macros. (We don't need to pull in all of syn + quote + proc_macro2 for simple literals)

Well for u256 you will need proc-macros anyway.

3 Likes

Why not lit = u256!123456789123456789123456789123456789? Quite true that it could be replace by a function, but I wonder if any case a macro can do that a function (const or not) cannot during compile time. I think maybe const solve this issue.

Just wondering can we do like f!"data: {data}" like python f-strings where we can pull external data with macro? Not sure if we can achieve that with rust function only.

2 Likes

Just to give a concrete example of something you can do with a macro but you can't with a function: You can turn a string literal into an Objective-C selector literal, like this: selector!("addObject:"). You can't do this in a function because a function can't add the right link_section and export_name attributes to the literal.

No, you can turn f!("{name}") into format!("{name}", name=name) with no problems (with proc macros), because you get the correct hygeine for the identifier by using the string literal's hygeine span.

https://crates.io/crates/fstrings

5 Likes

Javascript has a similar feature with tagged template strings:

tag`Hello ${a + b} world ${a * b}`
// is roughly equivalent to
tag(['Hello ', ' world '], a + b, a * b)

String interpolation in Scala works almost the same.

It would be nice to have an equally convenient syntax for string interpolation in Rust, such as

f!"Hello {a + b} world {a * b}"
4 Likes

I can't find the RFC again just now, but it is planned to make format!("x = {x}") work, and mean format!("x = {x}", x=x) (but just for single identifiers). Interestingly, if we just made macro!"lit" work for any macro, that would mean that you could write simple format strings as format!"x = {x}".

2 Likes

@CAD97 do you mean this RFC

1 Like

Oh, yes, that's right. It doesn't make me want prefix macros any more, though.

Obviously we can't have single token macro sugar and item-like macros.

I'm in favor of single token macro sugar. However, that leaves the macro_rules! compiler built-in, which would then need a special case in the parser, or would have to be deprecated.

1 Like