rfcs icon indicating copy to clipboard operation
rfcs copied to clipboard

better string interpolation

Open antonkulaga opened this issue 8 years ago • 28 comments

things like:

    let name = "Anton".to_string();
    print!("hello {}",name);

are akward and verbose. What about scala-like string interpolation that will work everywhere (not only in print statements)

    let name = "Anton".to_string();
    print!(s"hello {name}"); // Scala uses s"${variableName}" but I think $ is redundant

antonkulaga avatar Aug 12 '15 02:08 antonkulaga

We have almost this: https://doc.rust-lang.org/std/fmt/#named-parameters

steveklabnik avatar Aug 12 '15 20:08 steveklabnik

Making macro take arguments from the context would make them non-hygienic.

nagisa avatar Aug 12 '15 20:08 nagisa

Making macro take arguments from the context would make them non-hygienic.

There are ways to safely allow this kind of hygiene-bending, if we wanted to

nrc avatar Aug 25 '16 23:08 nrc

To add more examples in addition to Scala: Python 3.6 introduced a similar interpolation style (so called: literal string interpolation with f-strings), e.g.:

var1 = 23
s = f'hello {var1}'
print(s)

And Ruby also has a similar style - e.g.: "hello #{var1}"

gsauthof avatar Jan 15 '17 20:01 gsauthof

JavaScript too with template strings:

const name = 'Hodor'

console.log(`Hello, ${name}.`)

samhh avatar Jul 18 '17 13:07 samhh

I'm brand new to rust, coming from C#. I love C#'s string interpolation:

$"This {stringoutput} is an interpolated string" "This is not an interpolated string"

The difference is the interpolation is activated by the $ preceeding the quote. Inside the {} can be anything that can go on a single line and output a string.

I'd love to see something like this in rust.

damccull avatar Aug 28 '17 08:08 damccull

Sorry if this is annoying, but it's the same with me coming from Kotlin, which I love:

val age: Int = 10
println("I am $age years old")
println("Next year I will be ${age + 1} years old")

ChrisCooper avatar Nov 30 '17 20:11 ChrisCooper

I'd like to be able to write this :

println!("Value is {x}", x);

Instead of :

println!("Value is {x}", x=x);

FUB4R avatar Jan 19 '18 13:01 FUB4R

This would be very nice, it would also be nice if this supported none string formats in the cases where you now need to do: println!("{:?}", scores); so it would just be println!("{scores}", scores);

cdekok avatar Dec 27 '18 19:12 cdekok

Having strings name and surname interpolation is handy in many languages: s"Hello $name $surname" - Scala $"Hello {name} {surname}" - C# `Hello ${name} ${surname}` - Typescript and ES6 f"Hello {name} {surname}" - Python "Hello \(name) \(surname)" - Swift "Hello $name $surname" - Kotlin, Groovy and Powerhsell

Rust already checks { } during a string format. It may be convenient to use exising behavior and eliminate additonal signs (no $, s, f- or \( signs). Pure print!("hello {name} {surname}");

The good thing is that { } can be used for complex expressions, like it's done in Kotlin and C#: print!("version is {(v + 1)/2}");

An open question is how to describe string declaration, e.g. let str = "hello {name} {surname}" - this breaks current string declaration behavior, let str = format("hello {name} {surname}") - this is a bit lengthy.

ArturAmpilogov avatar Feb 14 '19 21:02 ArturAmpilogov

@ArturAmpilogov ... and let's not forget:

# echo "Hello $NAME $SURNAME" # bash and sh

I must confess it astonished me when I started in rust that a modern language did not not support string interpolation. Still surprises me :-)

vvaidy avatar Feb 14 '19 22:02 vvaidy

I know this issue is old, but string interpolation would make our lives better (more fun and easier).

# I prefer
outfile.write(
    format!("{x}\t{y}\t{z}\n")
        .as_bytes()
)

# over
outfile.write(
    format!("{}\t{}\t{}\n", x, y, z)
        .as_bytes()
)

# and
outfile.write(
    format!("{x}\t{y}\t{z}\n", x=x, y=y, z=z)
        .as_bytes()
)

The first one is much easier to inspect than the second and third versions.

enormandeau avatar Feb 26 '19 19:02 enormandeau

Could we write a macro that simulates the third version by typing @tcalvert's version?

injected_format!("{x}\t{y}\t{z}\n", x, y, z)
// expands to
format!("{x}\t{y}\t{z}\n", x=x, y=y, z=z)

Even if not in the standard, I'd like to know if a user can make his/her own injected_format!.

Although I don't like the redundancy in this syntax (why is there x again? is it the same x?), at least the order of the arguments doesn't have to match the order of usage, e.g.:

injected_format!("{x}\t{y}\t{z}\t{x}\n", z, y, x)
// expands to
format!("{x}\t{y}\t{z}\t{x}\n", z=z, y=y, x=x)

also works. Which means if you want to reorder the interpolated variables, you don't have to reorder them at two places.

Ideally we would have a macro that parses everything inside the braces and generates the named arguments for them, only once:

simple_format!("{x}\t{y}\t{z}\t{x}\n")
// expands to
format!("{x}\t{y}\t{z}\t{x}\n", x=x, y=y, z=z)

This can work, because evaluating simple variables should have no secondary effect (unless you write some tricky Display/Debug fmt method that modifies some static!), so the order in which we choose to put named arguments doesn't matter (we could just add them in the order we encounter the variables in the format pattern).

However, the macro would probably be much more complex, being able to handle cases like brace escaping ({{pure text}}).

hsandt avatar Apr 06 '19 16:04 hsandt

It turns out we can write a proc macro to do this now: https://crates.io/crates/ifmt. Only works on nightly with #![feature(proc_macro_hygiene)], but once proc macros are allowed in expression position it should work on stable (there are still some error-reporting kinks to be worked out, although hopefully writing such a string isn't too error-prone).

Handles expressions (iformat!("3+2={3 + 2}")), brace escaping ({{/}}), format specs ({x:?}), and even dirty nested raw strings: iprintln!(r##"{format!(r#"because why not? {}{}"#, ':', "}")}"##);

ct-austin avatar Apr 21 '19 00:04 ct-austin

Wow. That's pretty cool. I'm still new enough that I'm not sure how macros work, but I assume you just wrapped the basic ones in a new version that parses the string for formatting tags and replaces them?

damccull avatar Apr 21 '19 07:04 damccull

This may break backward compatibility.

tesuji avatar Apr 21 '19 10:04 tesuji

+1 for this feature, I'm new to Rust, and I think anything related to string manipulation is of paramount importance. Especially when it comes to making REST APIs or front-end and GUIs

chichid avatar May 03 '19 16:05 chichid

This may break backward compatibility.

But would it really? All of the formatting macros require parameters to be passed when the format string contains any {_}s.

let x = 42;
println!("x: {x}"); // error: there is no argument named `x`

Since it seems this would only affect code that is currently illegal, it should be trivial to have formatting macros that can handle both the current style and interpolated strings:

#[doc(hidden)]
pub use {std::println as _std_println, ifmt::iprintln as _ifmt_println};
#[macro_export]
macro_rules! println2 {
    ($fmt:expr) => ($crate::_ifmt_println!($fmt));
    ($fmt:expr, $($args:tt)*) => ($crate::_std_println!($fmt, $($args)*));
}
println2!("a number: {42}");
println2!("another number: {}", 360);
a number: 42
another number: 360

ExoticMatter avatar May 08 '19 18:05 ExoticMatter

I think JavaScript should interpolate string as Rust not the other way round , in rust inside {} parenthesis we can do a lot of trick on the interpolation , the only problem with rust interpolation is you have to track the position of the string. or if everybody still needs JS like interpolation then Rust should go with something like the angularjs interpolation {variable | formating} e.g {{ date | date : format : timezone }} (forget about double mustache and use single one of course)

serak avatar Jul 19 '19 12:07 serak

+1 Kotlin styled string interpolation

obvionaoe avatar Jun 20 '20 19:06 obvionaoe

Dup of #2795?

tesuji avatar Jun 21 '20 00:06 tesuji

@lzutao It appears that the PR you linked could be a proposed implementation for this issue. I'm not sure on the "Dup" thing though, since usually PRs that solve an issue are not considered "Dups". Most often people refer to one issue as a Dup of another if they are essentially both the same issue.

AndrewSav avatar Jun 21 '20 10:06 AndrewSav

@lzutao

That RFC is only for implicit capture of identifiers, supporting format!("{foo}") but no expressions beyond that.

I think this RFC should be used to discuss potential expansions to expression or dotted access interpolation, maybe using the suggested format!("{(a.b)}") syntax.

ct-austin avatar Jun 21 '20 22:06 ct-austin

Haven't tried yet but with some nightly features i think this exists here: https://github.com/anowell/interpolate

benorgera avatar Oct 15 '21 05:10 benorgera

@benorgera good job on string interpolation. I come from other languages ​​like C#/Java/Dart... among other languages, and I also think that string interpolation is kind of standard in modern languages ​​today, I feel rust is missing a bit on that, I see rust is very verbose for simple things, i think the compiler has to be smarter to make the programmer's life easier and not harder.

insinfo avatar Oct 20 '21 21:10 insinfo

This is now stable afaik, should be closed?

tyoc213 avatar Mar 12 '22 07:03 tyoc213

@tyoc213 can you provide some details please? (E,g. a link documenting the current state of the feature would be perfect)

AndrewSav avatar Mar 12 '22 10:03 AndrewSav

@AndrewSav probably referring to changes with Rust 1.58 (Jan 2022)? This page details what you can do fairly well.

This doesn't allow for expressions within the string (which the discussion here seems to have changed towards, but could be raised as a separate issue), you'd still need to do that externally, but is mostly the same beyond that.

polarathene avatar Mar 13 '22 01:03 polarathene

@rust-lang/lang What do you think about closing this?

frewsxcv avatar Mar 06 '23 06:03 frewsxcv

I agree with close -- https://blog.rust-lang.org/2022/01/13/Rust-1.58.0.html#captured-identifiers-in-format-strings covers what was in the OP here.

People can always make new IRLO or Zulip threads to discuss potential extensions, though I'm fairly sure it'll never be "any expression", and thus any proposal would need to justify a small-but-useful-enough addition.

scottmcm avatar Mar 06 '23 07:03 scottmcm