298

How do I concatenate the following combinations of types:

  • str and str
  • String and str
  • String and String
malbarbo
  • 9,223
  • 1
  • 30
  • 46
jsalter
  • 3,210
  • 2
  • 14
  • 9
  • 17
    Note that `str` and `&str` are *different types* and for 99% of the time, you only should care about `&str`. There are other questions detailing the differences between them. – Shepmaster May 10 '15 at 17:51
  • Does this answer your question? [How to concatenate static strings in Rust](https://stackoverflow.com/questions/35157399/how-to-concatenate-static-strings-in-rust) – Nathan McMillan Apr 05 '21 at 22:20

5 Answers5

361

When you concatenate strings, you need to allocate memory to store the result. The easiest to start with is String and &str:

fn main() {
    let mut owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    owned_string.push_str(borrowed_string);
    println!("{}", owned_string);
}

Here, we have an owned string that we can mutate. This is efficient as it potentially allows us to reuse the memory allocation. There's a similar case for String and String, as &String can be dereferenced as &str.

fn main() {
    let mut owned_string: String = "hello ".to_owned();
    let another_owned_string: String = "world".to_owned();
    
    owned_string.push_str(&another_owned_string);
    println!("{}", owned_string);
}

After this, another_owned_string is untouched (note no mut qualifier). There's another variant that consumes the String but doesn't require it to be mutable. This is an implementation of the Add trait that takes a String as the left-hand side and a &str as the right-hand side:

fn main() {
    let owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    let new_owned_string = owned_string + borrowed_string;
    println!("{}", new_owned_string);
}

Note that owned_string is no longer accessible after the call to +.

What if we wanted to produce a new string, leaving both untouched? The simplest way is to use format!:

fn main() {
    let borrowed_string: &str = "hello ";
    let another_borrowed_string: &str = "world";
    
    let together = format!("{}{}", borrowed_string, another_borrowed_string);

    // After https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html
    // let together = format!("{borrowed_string}{another_borrowed_string}");

    println!("{}", together);
}

Note that both input variables are immutable, so we know that they aren't touched. If we wanted to do the same thing for any combination of String, we can use the fact that String also can be formatted:

fn main() {
    let owned_string: String = "hello ".to_owned();
    let another_owned_string: String = "world".to_owned();
    
    let together = format!("{}{}", owned_string, another_owned_string);

    // After https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html
    // let together = format!("{owned_string}{another_owned_string}");
    println!("{}", together);
}

You don't have to use format! though. You can clone one string and append the other string to the new string:

fn main() {
    let owned_string: String = "hello ".to_owned();
    let borrowed_string: &str = "world";
    
    let together = owned_string.clone() + borrowed_string;
    println!("{}", together);
}

Note - all of the type specification I did is redundant - the compiler can infer all the types in play here. I added them simply to be clear to people new to Rust, as I expect this question to be popular with that group!

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
  • 2
    What do you think about `Add` / `+` symbol? You could cover it if you want. – bluss May 10 '15 at 23:48
  • Maybe that's simple enough, but understanding it requires looking at the possible type signatures for Add with String. – bluss May 10 '15 at 23:50
  • Thanks! Are you able to go into slightly more depth into how &String can be dereferenced as a &str? Which part of its implemention permits that and/or where does it say this in the doco? – jsalter May 12 '15 at 10:47
  • 1
    @jsalter that's a pretty separate topic, so it may be good as another top-level question. I have updated to link to the appropriate docs (as close as I can get, at least...) – Shepmaster May 12 '15 at 12:21
  • 13
    @ChrisMorgan It should be noted that the discrepancy `.to_owned()` and `.to_string()` has been fixed since the above comment thanks to impl specialization. They both now have the same performance when called on a `&str`. Relevant commit: https://github.com/rust-lang/rust/pull/32586/files – chad Nov 23 '16 at 02:30
  • The type specifications are definitely helpful in this code. – Zelphir Kaltstahl Oct 31 '17 at 15:24
  • Should 2nd sentence 2nd para read '... case for `String` and `&String`...' as it's an edit of less than 6 characters SO won't allow me to make this change! (or could be a more subtle point that I haven't understood) – paddyg Nov 17 '17 at 13:51
  • @paddyg Yes, it's kind of subtle. The starting types are both `String`, but then you take a reference to one (`&String`) which can be coerced to a `&str`. I put the entire path `String` -> `&String` -> `&str` because beginners may not even realize that you can take a reference to a `String`. :-) – Shepmaster Nov 17 '17 at 14:07
69

To concatenate multiple strings into a single string, separated by another character, there are a couple of ways.

The nicest I have seen is using the join method on an array:

fn main() {
    let a = "Hello";
    let b = "world";
    let result = [a, b].join("\n");

    print!("{}", result);
}

Depending on your use case you might also prefer more control:

fn main() {
    let a = "Hello";
    let b = "world";
    let result = format!("{}\n{}", a, b);

    print!("{}", result);
}

There are some more manual ways I have seen, some avoiding one or two allocations here and there. For readability purposes I find the above two to be sufficient.

Shepmaster
  • 274,917
  • 47
  • 731
  • 969
Simon Whitehead
  • 57,414
  • 7
  • 93
  • 127
  • Where is `join` documented? It seems to sit halfway between an Array and a String. I searched through the [array](https://doc.rust-lang.org/std/primitive.array.html) documentation and was quickly confused. – Duane J Jan 10 '18 at 21:51
  • 3
    @DuaneJ `join` is actually attached to [the `SliceContactExt` trait](https://doc.rust-lang.org/std/slice/trait.SliceConcatExt.html). The trait is marked unstable but its methods are stable and [are included in the Prelude](https://doc.rust-lang.org/std/prelude/#prelude-contents) so they're usable everywhere by default. The team appear to be well aware this trait does not need to exist and I imagine things will change in future with it. – Simon Whitehead Jan 10 '18 at 22:45
  • Perhaps you should mention that `join` is more efficient than `s1.to_owned().push_str(s2)` for concatenating two `str`'s as it avoids the second allocation. – Ibraheem Ahmed Mar 19 '21 at 15:36
16

I think that concat method and + should be mentioned here as well:

assert_eq!(
  ("My".to_owned() + " " + "string"),
  ["My", " ", "string"].concat()
);

and there is also concat! macro but only for literals:

let s = concat!("test", 10, 'b', true);
assert_eq!(s, "test10btrue");
suside
  • 367
  • 3
  • 7
  • `+` is already mentioned in an [existing answer](https://stackoverflow.com/a/30154791/155423). (*This is an implementation of the `Add` trait that takes a `String` as the left-hand side and a `&str` as the right-hand side:*) – Shepmaster Jan 14 '20 at 15:19
  • 2
    True, _existing answer_ is so broad I didn't notice though. – suside Jan 15 '20 at 18:14
  • 2
    Best answer so far. Just use array method or concat for strings. Macros are just handy for hiding some syntax rather than inventing complex syntax making core language cryptic. Add trait could be nice for objects but can be confusing at least. – Anssi Jul 31 '20 at 09:32
16

Simple ways to concatenate strings in Rust

There are various methods available in Rust to concatenate strings

First method (Using concat!() ):

fn main() {
    println!("{}", concat!("a", "b"))
}

The output of the above code is :

ab


Second method (using push_str() and + operator):

fn main() {
    let mut _a = "a".to_string();
    let _b = "b".to_string();
    let _c = "c".to_string();

    _a.push_str(&_b);

    println!("{}", _a);

    println!("{}", _a + &_c);
}

The output of the above code is:

ab

abc


Third method (Using format!()):

fn main() {
    let mut _a = "a".to_string();
    let _b = "b".to_string();
    let _c = format!("{}{}", _a, _b);

    println!("{}", _c);
}

The output of the above code is :

ab

Check it out and experiment with Rust playground.

Matthias Braun
  • 24,493
  • 16
  • 114
  • 144
ASHWIN RAJEEV
  • 1,223
  • 1
  • 11
  • 20
6

2020 Update: Concatenation by String Interpolation

RFC 2795 issued 2019-10-27: Suggests support for implicit arguments to do what many people would know as "string interpolation" -- a way of embedding arguments within a string to concatenate them.

RFC: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html

Latest issue status can be found here: https://github.com/rust-lang/rust/issues/67984

At the time of this writing (2020-9-24), I believe this feature should be available in the Rust Nightly build.

This will allow you to concatenate via the following shorthand:

format_args!("hello {person}")

It is equivalent to this:

format_args!("hello {person}", person=person)

There is also the "ifmt" crate, which provides its own kind of string interpolation:

https://crates.io/crates/ifmt

JamesHoux
  • 1,985
  • 2
  • 19
  • 40