Does Rust support Ruby-like string interpolation?
Asked Answered
E

4

89

In Ruby I could do this.

aaa = "AAA"
bbb = "BBB #{aaa}"

puts(bbb)

> "BBB AAA"

The point of this syntax is eliminating repetition, and making it to feel like a shell script - great for heavy string manipulation.

Does Rust support this? Or have plan to support this? Or have some feature which can mimic this?

Eyrir answered 24/1, 2014 at 4:47 Comment(2)
There is a Rust RFC for this. Feel free to upvote. โ€“ Kymry
Looks like the RFC is nearly stable now. github.com/rust-lang/rust/pull/90473 โ€“ Socinian
E
98

This is available since Rust 1.58! Here's the syntax:

let (person, species, name) = ("Charlie Brown", "dog", "Snoopy");

// implicit named argument `person`
print!("Hello {person}");

// implicit named arguments `species` and `name`
format!("The {species}'s name is {name}.");

RFC 2795 is the original proposal.

Eyrir answered 20/7, 2020 at 16:15 Comment(6)
IMO coming from JS that RFC is ๐Ÿคฎ. The mentioned full-interpolation syntax {( )} has awful readability, especially compared to ${ }, and the author has a mysteriously strong bias against supporting interpolation of arbitrary expressions. Instead they'd rather wrestle with the ways string concatenation throws a wrench into the macro formatting system. Having a string literal syntax where arbitrary expressions can be embedded in ${ } is just so much simpler. โ€“ Flofloat
It was release in Rust version 1.58.0 ๐Ÿ™Œ โ€“ Floats
@Andy, you are missing the point of Rust's type system, safety, and compile time macros. See this SO answer for details. โ€“ Undertaker
I don't see how it would be impossible to type check arbitrary expressions embedded inside string template literals. It seems like the main problem is they want to stick with using macros to do formatting on a plain old string template instead of adding a new string template literal syntax to the language grammar. โ€“ Flofloat
@Flofloat The thing is, type checking is hard, lifetime checking is hard. And when it's too hard, you can't tell the compiler "trust me, this value is of this type" like in TypeScript, happily confusing casting and annotation because they don't matter anyway. Instead, in most cases for types and all cases for lifetimes, you gently tell it :"could we make it so this variable is of this type ?". And if you can, you have type safety. So forcing to use a variable prevents the risk of needing a refactor for an obscure reason if the expression is for example an &str โ€“ Boisleduc
@Flofloat But I think the main reason they are cautious about it, beyond keeping scoping and type analysis simple for both human and compiler, is design space. Expressions can already contain quotes, braces and even colon. I can't think of a collision, but break label ('lbl : { ...) comes dangerously close (is that a label colon, or a formatting one ?). Now a lot of things are still possible, but Rust devs are extremely cautious when it comes to commit to new features and syntaxes. They probably learned from JavaScript willy-nilly adding every next cool fad and falling into legacy hell. โ€“ Boisleduc
B
82

Rust has string formatting.

fn main() {
    let a = "AAA";
    let b = format!("BBB {}", a);
    println(b);
}
// output: BBB AAA

In the Rust version, there is no additional repetition but you must explicitly call format!() and the inserted values are separated from the string. This is basically the same way that Python and C# developers are used to doing things, and the rationale is that this technique makes it easier to localize code into other languages.

The Rust mailing list has an archived discussion ([rust-dev] Suggestions) in which the different types of string interpolation are discussed.

Baziotes answered 24/1, 2014 at 4:54 Comment(8)
It seems the discussion didn't continue much. Sad. โ€“ Eyrir
@ChrisMorgan: That's just background information. The background information hasn't changed in the past 23 months, unless you have something you'd like to share. โ€“ Baziotes
You can also use format!("BBB {name}", name = a). (It would probably actually be possible to use the same parsing & macro infrastructure as format uses to achieve inline_fmt!("BBB {a}"), although hygiene may require some tricks to work around.) โ€“ Physiotherapy
@DietrichEpp: in 23 months a lot has changed about the language; things that were considered infeasible or inappropriate then may not be any more. For example, back then printf-formatting was the way things were done while now it is the new style formatting. There is also a much larger community now. These all change the dynamics considerably. โ€“ Buonomo
@ChrisMorgan: We're talking about different things here. I'm talking about the background information: "How do other languages implement string formatting, and why would we choose one syntax over the other?" That hasn't changed. โ€“ Baziotes
Both Python and C# have since added string interpolation. Python: f'{a} times {b} is {a * b}.', C#: $"{a} times {b} is {a * b}" โ€“ Simplistic
Here is a more recent discussion on this topic: github.com/rust-lang/rfcs/issues/1250. This answer currently doesnโ€™t feel relevant anymore, since it justifies Rustโ€™s string formatting as being about portability to other languages, which is no longer true โ€“ Python, C#, JS, etc, all have syntaxes closer to Ruby than the Rust one. โ€“ Snippy
This doesn't work on constants (at the time of this writing). โ€“ Noodlehead
R
47

As of Rust 1.58, you can take advantage of captured identifiers in format strings. That lets you do stuff like this:

let msg = "here";
let s = format!("Abc {msg}");  
println!("Hi t{msg}");   // prints "Hi there"
println!("{s}");         // prints "Abc here" 

This feature can be seen as a subset of string interpolation. Expressions like format!("Abc {a+b}") are not supported and it's unclear whether such a feature will ever be added. There have also been discussions on adding a subset of expressions for dotted paths, as in format!("Abc {foo.bar}").

Also note that the Rust 2021 Edition makes room for future additions to the language, such as f"hello {name}", which would allow for a much more concise string interpolation, comparable to most other modern languages.

Recommend answered 28/12, 2021 at 7:43 Comment(0)
E
0

No it does not. While Rust 1.58 added "captured identifiers in format strings", it's not string interpolation. Instead, it lets you use the current scope as the keyword arguments to the println! macro -- so it's a shortcut to passing those named arguments:

let name = "eonil";
// These two are equivalent:
println!("Hi {name}!");
println!("Hi {name}!", name = name);

This answer explains it in depth.

Since it's only syntatic sugar for formatting macros, it doesn't work like general string interpolation:

let revision = 34343;
let output = Command::new("TortoiseProc.exe")
    .arg("/command:log")
    .arg("/startrev:{revision}")
// launches uninterpolated "TortoiseProc.exe /command:log /startrev:{revision}"

let output = Command::new("TortoiseProc.exe")
    .arg("/command:log")
    .arg(format!("/startrev:{revision}"))  // using the macro
// launches intended "TortoiseProc.exe /command:log /startrev:34343"
Eph answered 25/3 at 5:22 Comment(0)

© 2022 - 2024 โ€” McMap. All rights reserved.