String literal equivalence

Hello,

I am wrestling with the format! macro and could use some explanation on why, at a type level, (&'static str) != (typeof("string literal")).

    let pattern:&'static str = "{}";

In format! I can use "{}" as the format string but can not substitute pattern which, in some things I've read, imply they are the same.

Playground.

Thanks!

Macros are evaluated long before the type system even starts up in the compiler: they get access to the text of their call and produce new program text to go in its place. Most of the time, the macro arguments are copied into the output to be dealt with in later stages.

format!, on the other hand, needs to look at the contents of the format string to determine the code that needs to be produced. That means it needs to be present in the text of the format call instead of a reference to something defined elsewhere.

3 Likes

In other words, although format! looks like a macro, it is not a macro; it is actually compiler magic whose arguments are resolved at a later point in the compilation chain.

Ok! Thank you both for the clarification. And 2e71828 I was asking about this while I was trying to implement your closure solution for the other open question I have :).

Is there a standard, safe replacement for format! to be able to to set a pattern string at runtime? I found runtime-fmt but it didn't compile successfully for me.

Though format has its share of compiler magic, the underlying issue is fundamental to how Rust macros are processed. Macros can’t look at variable values themselves, though they can produce code that does. For example, this shows essentially the same behavior:

macro_rules! digit_to_char {
    (0) => { '0' };
    (1) => { '1' };
    /* ... */
    (9) => { '9' };
}

#[test]
fn test(){
    // this works
    assert_eq!(digit_to_char!(1), '1');

    // this doesn't
    const X:u8 = 1;
    assert_eq!(digit_to_char!(X), '1');
}

error: no rules expected the token `X`
  --> src/lib.rs:15:31
   |
1  | macro_rules! digit_to_char {
   | -------------------------- when calling this macro
...
15 |     assert_eq!(digit_to_char!(X), '1');
   |                               ^ no rules expected this token in macro call

(Playground)

format! is essentially the same, except there’s an infinite number of match arms in its ruleset (one for each valid format string).


I don’t do much string manipulation in Rust, so I’m not familiar with the available crates, unfortunately.

That's an interesting way to view the process. If Rust ever gets an additional form of macro-like meta-programming that is invoked later in the compilation process, probably working on the HIR or MIR, then it might be possible to implement format! using that extended meta-programming.

It's not a macro_rules! macro, but that doesn't mean it's not a macro.

As of Rust 1.45 you can use proc macros in expression position (and there was proc_macro_hack before that). So format! could be a normal proc macro -- looking at a string and generating code based on that is exactly what proc macros can do.

2 Likes

I had thought that a query in a prior discussion thread had stated that proc macros were up to the task. However, discussion upstream in this thread convinced me that my memory was incorrect. Thanks for the correction / clarification.