rfcs
rfcs copied to clipboard
better string interpolation
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
We have almost this: https://doc.rust-lang.org/std/fmt/#named-parameters
Making macro take arguments from the context would make them non-hygienic.
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
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}"
JavaScript too with template strings:
const name = 'Hodor'
console.log(`Hello, ${name}.`)
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.
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")
I'd like to be able to write this :
println!("Value is {x}", x);
Instead of :
println!("Value is {x}", x=x);
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);
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 ... 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 :-)
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.
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}}
).
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? {}{}"#, ':', "}")}"##);
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?
This may break backward compatibility.
+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
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
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)
+1 Kotlin styled string interpolation
Dup of #2795?
@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.
@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.
Haven't tried yet but with some nightly features i think this exists here: https://github.com/anowell/interpolate
@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.
This is now stable afaik, should be closed?
@tyoc213 can you provide some details please? (E,g. a link documenting the current state of the feature would be perfect)
@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.
@rust-lang/lang What do you think about closing this?
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.