Rust String Concatenation
Concatenation joins text pieces into one String. push_str borrows, + moves its left side, and format! borrows everything.
Grow a String with push_str
push_str borrows the text you add — the text stays fully usable afterwards:
let mut s = String::from("foo");
let extra = "bar";
s.push_str(extra);
println!("{} and extra is still {}", s, extra);Add one char with push
push adds a single character at the end:
let mut s = String::from("note");
s.push('!');
println!("{}", s);Join two Strings with +
The left String moves into the result and is gone. The right side is only borrowed. So write &right:
let left = String::from("Hello, ");
let right = String::from("world!");
let joined = left + &right;
println!("{} and right is still {}", joined, right);Chain + for a quick line
Chaining + works — but it gets hard to read after two joins:
let a = String::from("tic");
let b = String::from("tac");
let c = String::from("toe");
println!("{}", a + "-" + &b + "-" + &c);format! borrows everything
format! joins many pieces and moves none of them — every value stays usable:
let a = String::from("small");
let b = String::from("medium");
let c = String::from("large");
println!("{}", format!("{a}-{b}-{c}"));
println!("all still here: {} {} {}", a, b, c);Example
fn main() {
let mut s = String::from("foo");
let extra = "bar";
s.push_str(extra);
println!("{} / extra still usable: {}", s, extra);
let left = String::from("Hello, ");
let right = String::from("world!");
let joined = left + &right;
println!("{} / right still usable: {}", joined, right);
let a = String::from("tic");
let b = String::from("tac");
let c = String::from("toe");
println!("{}", format!("{a}-{b}-{c}"));
}Key points
- Every joining tool ends with one String: the tools differ only in what happens to the inputs.
- The right side of + is a reference.
- Write &s2, not s2.
- Rust turns &String into &str automatically.
- This trick is called deref coercion.
In this example, three joins show three ownership stories. push_str borrows extra, so extra prints again afterwards, and s is now foobar. Next, left + &right moves left into joined — left is gone — but right was only borrowed, so it still prints: world!. Finally, format! builds tic-tac-toe from a, b and c while borrowing all three. Pick push_str to grow one String, + for one quick join, and format! when the pieces pile up.
Note
In many languages + copies both sides and leaves them alone.
In Rust, + reuses the left String as the base of the result — that is why the left side moves.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
After let s3 = s1 + &s2;, which values can you still use?