String::push_str() / push() / +
| Since: | Rust 1.0(2015) |
|---|
To append to a Rust String, you can use push_str() (append a string slice), push() (append a single character), the + operator, or the format!() macro. Each method handles ownership differently.
Syntax
let mut s = String::from("Hello");
// Appends a &str to the end (ownership is not moved).
s.push_str(", World"); // s = "Hello, World"
// Appends a single char to the end.
s.push('!'); // s = "Hello, World!"
// + operator: consumes ownership of the left operand and concatenates.
let s1 = String::from("Hello");
let s2 = String::from(", World");
let s3 = s1 + &s2; // s1's ownership is moved. s2 is borrowed.
// s1 can no longer be used here. s3 = "Hello, World"
// format! macro: concatenates while keeping ownership of all variables.
let s4 = format!("{}-{}-{}", "Hello", "World", "Rust");
Method List
| Method / Syntax | Description |
|---|---|
| s.push_str(str) | Appends a &str to the end. Ownership is not moved. |
| s.push(char) | Appends a single char to the end. |
| s1 + &s2 | Concatenates s1 (ownership moved) and s2 (borrowed). |
| format!("{}{}", s1, s2) | Concatenates multiple strings while retaining ownership of all. |
| s.extend(iter) | Appends elements from an iterator to the string. |
| s.repeat(n) | Returns a new String with the content repeated n times. |
| s.insert(idx, char) | Inserts a char at the given byte index idx. |
| s.insert_str(idx, str) | Inserts a &str at the given byte index idx. |
Sample Code
sample_string_push_str.rs
fn main() {
// push_str: appends a string (the variable must be mutable).
let mut s = String::from("Hello");
s.push_str(", Rust");
s.push('!');
println!("push_str + push: {}", s);
// Concatenation with the + operator (watch out for ownership).
let s1 = String::from("Hello");
let s2 = String::from(", World");
let s3 = s1 + &s2; // s1's ownership is consumed.
// println!("{}", s1); // ← Error! s1 can no longer be used.
println!("+ operator: {}", s3);
// Chaining multiple + calls becomes hard to read.
let s4 = String::from("tic");
let s5 = String::from("tac");
let s6 = String::from("toe");
// s4 + &s5 + &s6 is hard to read, so format! is recommended.
let s7 = format!("{}-{}-{}", s4, s5, s6); // Ownership is not moved.
println!("format!: {}", s7);
println!("s4: {}", s4); // With format!, ownership is retained.
// extend: appends from an iterator.
let mut s = String::from("Hello");
s.extend([',', ' ', 'R', 'u', 's', 't']);
println!("extend: {}", s);
// repeat: repeats the string.
let repeated = "ha".repeat(3);
println!("repeat: {}", repeated); // hahaha
// insert / insert_str: inserts in the middle.
let mut s = String::from("Hello!");
s.insert(5, ','); // Inserts a comma at index 5.
s.insert_str(6, " World"); // Inserts a string at index 6.
println!("insert: {}", s); // Hello, World!
// Common pattern: building a string incrementally.
let members = ["user_a", "user_b", "user_c"];
let mut result = String::new();
for (i, member) in members.iter().enumerate() {
if i > 0 {
result.push_str(", ");
}
result.push_str(member);
}
println!("Result: {}", result);
}
Compile with the following command:
rustc string_push_str.rs ./string_push_str push_str + push: Hello, Rust! + operator: Hello, World format!: tic-tac-toe s4: tic extend: Hello, Rust repeat: hahaha insert: Hello, World! Result: user_a, user_b, user_c
Common Mistakes
Mistake 1: Compile error from consuming the left operand's ownership with +
The + operator consumes ownership of the left-hand String. Trying to use the left-hand String after a + concatenation will cause a compile error.
string_push_mistake1.rs
fn main() {
let s1 = String::from("Hello");
let s2 = String::from(", World");
let s3 = s1 + &s2; // s1's ownership is consumed.
// s1 can no longer be used.
// println!("{}", s1); // compile error: borrow of moved value
println!("{}", s3);
println!("{}", s2); // s2 was only borrowed as &s2, so it is still valid.
}
Compile with the following command:
rustc string_push_mistake1.rs ./string_push_mistake1 Hello, World , World
Use format!() or clone() when the original String needs to remain available.
string_push_fix1.rs
fn main() {
let s1 = String::from("Hello");
let s2 = String::from(", World");
// format!() does not consume ownership.
let s3 = format!("{}{}", s1, s2);
// Both s1 and s2 are still usable.
println!("s1={}", s1);
println!("s2={}", s2);
println!("s3={}", s3);
}
Compile with the following command:
rustc string_push_fix1.rs ./string_push_fix1 s1=Hello s2=, World s3=Hello, World
Mistake 2: Mixing up the argument types for push_str and push
push_str() takes a &str (a string slice), while push() takes a char (a single character). Passing a char to push_str will cause a compile error.
string_push_mistake2.rs
fn main() {
let mut s = String::from("Hello");
// push_str takes &str.
s.push_str(", World"); // OK
// s.push_str('!'); // error: expected &str, not char
// push takes char.
s.push('!'); // OK
// s.push("!"); // error: expected char, not &str
println!("{}", s); // Hello, World!
}
Compile with the following command:
rustc string_push_mistake2.rs ./string_push_mistake2 Hello, World!
Notes
When choosing how to concatenate strings, use format!() for readable multi-string joins. Use push_str() when building a string incrementally in a loop. If performance matters, pre-allocate memory with String::with_capacity() to avoid repeated reallocations.
The + operator consumes ownership of the left-hand String. If you need to use the left-hand variable afterward, use format!() instead.
For ways to create a String, see String::from() / to_string().
If you find any errors or copyright issues, please contact us.