Why doesn't Rust expand f-string beyong macros?

With the release of Rust 1.58, users can use f-string in macros which accept formatting strings. This is still quite limited. I wonder why doesn't Rust introduce something like Python's f-string so that users can use it beyond macros. For example, in any place users would manually call format!, they can just use a syntax sugar f"...". Of course, we can limit such syntax sugar to accept only variable interpolation instead of arbitrary expression interpolation.

There is no value in writing f"string" over format!("string"). Keeping formatting as a macro has the benefit that it is consistent with the rest of the language and doesn't need a separate feature.

2 Likes

What would be the point? format!("...") is quite concise for how much work it does, and I don't see a pressing need to shorten it.

But if you want a "why not", note that the 1.58 change was to format_args! and so all the formatting macros (write!, println!, etc.) can take advantage of it. Pythonesque f-strings would really only be meaningful as a shortening of format!, which is a much smaller scope. Unlike in Python, in Rust we often write formatted output directly to a stream or a file or buffer or something, without stopping to put it in a String first, which is often a waste of time and memory (and is also not in core). A convenient syntax for f-strings would be a trap for people who do not understand this, since they would tend to use the more concise syntax over the more performant one.

6 Likes

Other than turning the "why not" into the "why do it," I would think the obvious problem with this is that format! allocates a String. If f"..." were a part of the language, then you'd have to say what that syntax does when String is unavailable. There is no String in core, and yet Rust runs just fine using only core. You could say, "well if String isn't available then it returns a compilation error." Which is maybe fine. It would also make String more special than it is, unless you went even further and made f"..." syntax extensible. But that's a big ol' can of worms.

I think it would also be the first feature of the language that had to be explicitly aware of allocation. (Maybe Box is an exception to that, not quite sure.)

16 Likes

The short answer to why Rust doesn't have this is that it hasn't been implemented yet. The syntax required for such a feature was reserved in the 2021 edition (and without this it would be a breaking change to add it). There's a suggestion in the edition guide that it could be added as an abbreviation of format! but, as others have noted, that has limited utility and would be inconsistent with the other formatting macros, so I'm not sure there's a huge appetite to add it any time soon.

1 Like

I think it'd be likely that f"..." would give a Arguments in std::fmt - Rust rather than a String.

That way it could be passed to various functions instead of them all being macros -- like println(f"hello {name}") could work, or maybe f"hello {name}".to_string().

Is that worth doing? I dunno. But it solves the String problem and avoids a bunch of the current "that defacto has to be a macro, not a function" problems.

14 Likes

The link below was posted on r/rust recently, which might be relevant. A use alias would then allow f!"blah blah {varname}" if this RFC makes it to a stable release.

5 Likes

That's pretty useless. The valuable part of string interpolation isn't the short prefix or the lack of brackets (that part is trivial), it's being able to interpolate more complex expressions directly into the text, without creating one-off intermediate variables.

The real answer, besides the fact that no one did it yet, is that allowing arbitrary expressions complicates the grammar and semantics beyond what can easily be handled with a macro. It's certainly not impossible, plenty of languages did it, but it warrants significant design work and a separate language feature, not just a smarter macro.

3 Likes

I think that f"hello {name}".to_string() actually defeats the usefulness of f-strings.

I can clearly see the issue with String and core. But if f-strings had to produce format args, then I think they are not worth the time and effort.

2 Likes

Personally, I don't find a to_string() call obnoxious. It would also matter even less if the string was longer, maybe even spanning several lines. For me the main benefit of f-strings would be simplified usage of array subscripts and nested field accesses in formatting, and a trailing function call doesn't make it any less useful.

Maybe f-strings could rely on some type inference magic to automatically give you a value of the correct type, provided the type is known.

You can have expanded interpolation without f-strings, though, just by changing format_args!. I don't imagine f-strings would be added just to support more interpolation in format strings without backporting that feature into format! and println! etc. This thread is about the syntax f"..." replacing format!("..."), not about what expressions you can put in {}.

3 Likes