20

In the nightly Rust it is no longer possible to designate a string literal as String with a "~" character.

In C++, for example, I'm using user-defined literals to concatenate string literals without the crust of mentioning std::string every time:

inline std::string operator"" _s (const char* str, size_t size) {return std::string (str, size);}
foo ("Hello, "_s + "world!");

Is there a similar feature existing or planned in Rust to make string literal concatenation less painful than String::from_str ("Hello, ") + "world!"?

Community
  • 1
  • 1
ArtemGr
  • 11,684
  • 3
  • 52
  • 85
  • The simple answer for now is “no”. May I ask why you *want* to do this? – Chris Morgan Aug 15 '14 at 14:38
  • 1
    To improve readability. – ArtemGr Aug 15 '14 at 14:42
  • @ChrisMorgan In a wider context, user-defined literals promote strong typing by making it less painful to introduce and read the types in the code. It's the same way Rust uses `123u32` `123i64` instead of `static_cast(123)` to promote strong typing. Now, writing `String::from_str` every time is like writing `static_cast(123)` every time. – ArtemGr Aug 15 '14 at 15:23
  • 2
    Why do you want to concatenate string literals? Can you just write them out as a single literal? – huon Aug 15 '14 at 21:38
  • 1
    @dbaupp You've got to be kidding me. Of course not! Concatenating string literals is my bread and butter, what would I *do* at work if I just write them as a single literal? – ArtemGr Aug 16 '14 at 05:26
  • I believe that in C and C++ string _literals_ will concatenate if you write them consequently like `char foo[] = "Hello " "world";` will produce `"Hello world". So there is no need for used-defined literal. – Amomum Oct 29 '19 at 23:02
  • @Amomum That's not a case in the Rust proper, though [fomat-macros](https://crates.io/crates/fomat-macros) allows for this (and is my favorite way of building strings at present). Example: https://github.com/KomodoPlatform/atomicDEX-API/blob/841f82e8d0f30eb1e602e4317937060feb0bd675/mm2src/mm2.rs#L88 – ArtemGr Oct 30 '19 at 00:37
  • @ArtemGr ok, I just wanted to be sure you know that in C++ you don't have to use UDL for this, since string literal concatenation is not very widely known feature. – Amomum Oct 30 '19 at 09:32

3 Answers3

42

If you literally (hah) have string literals, you can use the concat! macro:

let lit = concat!("Hello, ", "world!")

You can natively split strings over several lines:

let lit = "Hello, \
           World";

The \ consumes all following whitespace, including the leading spaces on the next line; omitting the \ will include the string data "verbatim", with newlines and leading spaces etc.

You can add a &str to a String:

let s = "foo".to_string() + "bar" + "baz";

You could use push_str iteratively:

let mut s = "foo".to_string();
s.push_str("bar");
s.push_str("baz");

You could use SliceConcatExt::concat:

let s = ["foo", "bar", "baz"].concat();

If all else fails, you can define a macro to do exactly what you want.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
huon
  • 94,605
  • 21
  • 231
  • 225
17

You can use the format! macro. It is more readable, more translation-friendly, more efficient, and more powerful (you can concatenate more than just strings, just like C++'s ostringstream). It is also completely type-safe.

format!("Hello, {}", "world!")

You can also use named arguments to improve readability.

format!("hello, {who}", who = "world")

The full formatting syntax is described in std::fmt.

Rust does not have user-defined literals. I think adding such a feature is backward-compatible, so maybe this feature will be added after Rust 1.0.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
barjak
  • 10,842
  • 3
  • 33
  • 47
  • Thanks for the answer. I like how Rust makes such things possible, but if we speak about the program readability then these printf variants all suffer from the problem of shuffling things around instead of keeping them in the natural order. When building a complex string, such as an SQL query, a version that keeps the things in place, like the one described here http://blog.moertel.com/posts/2006-10-18-a-type-based-solution-to-the-strings-problem.html (safe-string kernel), is preferable. – ArtemGr Aug 16 '14 at 05:45
  • I should also mention the string interpolation in Scala that avoids the shuffling problem as well: http://slick.typesafe.com/doc/2.0.0-M3/sql.html#string-interpolation - the variables are automatically escaped there (or rather, bound into a prepared query) pretty much like in the Haskell version, although the former is more powerful. – ArtemGr Aug 16 '14 at 06:17
  • 1
    @ArtemGr, it should be possible to write a macro that does preparation/interpolation in a similar way ([example of syntax](https://github.com/rust-lang/rust/issues/14658#issuecomment-45161551)); the type system is certainly powerful enough to express the concepts like safe strings. – huon Aug 16 '14 at 07:11
  • @dbaupp That's a music to my ears, I might invest some time into learning the Rust macro system then, thanks. – ArtemGr Aug 16 '14 at 07:23
2

You can concatenate str literals without using String with the concat! macro:

let input = concat!("Hello", ' ', "world");

To make it a string, specify the destination type and use into:

let input: String = concat!("Hello", ' ', "world").into();

Full program:

fn main() {
    let input: String = concat!("Hello", ' ', "world").into();
    println!("{}", input);  // Output: Hello world
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
matiu
  • 7,469
  • 4
  • 44
  • 48
  • `concat!` is already shown by the [highest-voted answer](https://stackoverflow.com/a/25337811/155423). This answer doesn't really add anything valuable. – Shepmaster Mar 11 '19 at 23:50