🎓 A1Learner
🔍
0 of 142 lessons completed

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?