Start learning free

LESSON 59 OF 414 · STRINGS I

String Concatenation in Rust: +, += and push_str

Lesson Read the full explanation

Remember that string concatenation is used to dynamically generate messages that can be different depending on context.

We most often use it with variables, let's check an example:

let adjective = "awesome";
let mut message = String::from("AlgoCademy is ");

// String concatenation & assignment:
message = message + adjective;

println!("{}", message); // Output: AlgoCademy is awesome

We first concatenate message and adjective and then we assign the result to message. Note that + moves the String on its left, which is why we assign the result back to message.


Concatenating with +=

Since this is such a common pattern, there is the += operator which does both the concatenation and assignment in one step.

let adjective = "awesome";
let mut message = String::from("AlgoCademy is ");

// With += operator:
message += adjective;

println!("{}", message); // Output: AlgoCademy is awesome

Concatenating multiple strings:

We can append as many strings as we want using the += operator:

let name = "Andy";
let pet = "dog";
let mut message = String::from("Hey, ");

message += name;
message += "! Nice ";
message += pet;
message += "!" ;

println!("{}", message); // Output: Hey, Andy! Nice dog!


Assignment
Follow the Coding Tutorial and let's practice with string concatenation!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 3
    Ctrl + Enter to check

    RUST · STRINGS I

    String Concatenation Ii in Rust, explained

    Why Rust strings need care

    Building a message out of pieces is one of the most common jobs in a program. In Rust it works a little differently than in most languages, because there are two string types. A string literal such as "awesome" is a &str, a borrowed, read-only view of text that can't grow. A String owns its text in a buffer that can. Concatenation always means growing a String, so the left-hand side has to be one.

    Concatenating with +

    The + operator takes a String on the left and a &str on the right, and returns a String:

    let adjective = "awesome";
    let mut message = String::from("AlgoCademy is ");
    message = message + adjective;
    println!("{}", message);
    
    AlgoCademy is awesome
    

    The expression takes message by value. It appends adjective to the same buffer, and the result is assigned back to message. The variable is declared mut because it is assigned a second time.

    Concatenating with +=

    Since "append to a variable" is so common, += does the append and the assignment in one step:

    let mut message = String::from("Hey, ");
    message += "Andy";
    message += "! Nice ";
    message += "dog";
    message += "!";
    println!("{}", message);
    
    Hey, Andy! Nice dog!
    

    You can append as many pieces as you like. The right-hand side must be a &str: a literal, or a &String borrowed from another string.

    Concatenating with push_str

    The method push_str appends a string slice to a String in place. It does the same job as += and is the form the lesson's exercise uses:

    let mut text = String::from("ab");
    text.push_str("cd");
    text.push('e');
    println!("{}", text);
    
    abcde
    

    A single character is appended with push, whose argument is a char in single quotes. push_str takes a whole string in double quotes.

    Because these forms write into the existing buffer, they are the natural choice inside a loop that builds one long string. The buffer grows when it needs to, and the old text is never copied into a brand new string for every piece.

    Mistakes the compiler catches

    • Two literals. let c = "Hello, " + "world"; fails with cannot add `&str` to `&str`, and the compiler notes that concatenation requires an owned String on the left. Start from String::from(...) or .to_string().
    • A String on the right. a + b with two String values reports expected `&str`, found `String`. Borrow the second one: a + &b.
    • Using the left side afterwards. let b = a + "x"; moves a into the operation. Printing a after that fails with borrow of moved value: `a`. If you need both, write a.clone() + "x", or build the result with format!("{}x", a).
    • Appending to a literal. With let mut message = "Coding is "; the variable holds a &str, and message.push_str("fun") fails with no method named `push_str` found for reference `&str`. Making the variable mut doesn't help; the type has to be String, which is why the starter code calls .to_string().

    Appending numbers

    Only text can be appended. message += 42 is rejected with expected `&str`, found integer, because Rust never converts a number to text silently. Convert it first with to_string() and borrow the result:

    let n = 42;
    let mut s = String::from("n = ");
    s += &n.to_string();
    println!("{}", s);
    
    n = 42
    

    The explicit conversion is typical of Rust: the cost of building a new string is visible in the code, and the type of every piece is clear to the reader. When the pieces are mostly numbers, format!("n = {}", n) does the conversion for you.

    Joining many pieces with format!

    When a message mixes several values, format! is often clearer than a chain of +. It takes the same arguments as println! but returns a String instead of printing, and it doesn't consume its arguments:

    let name = String::from("Andy");
    let greeting = format!("Hi, {}!", name);
    println!("{} {}", greeting, name);
    
    Hi, Andy! Andy
    

    The lesson's exercise

    The starter code declares message as a mutable String made from a literal and adjective as a &str. The exercise has three steps.

    1. Append the adjective. Call message.push_str(&adjective). Passing &adjective borrows the variable, and Rust converts the resulting &&str to the &str that push_str expects.
    2. Append the ending. Call push_str again with ", isn't it?". The apostrophe inside a double-quoted literal needs no escape.
    3. Print the result with println!("{}", message).
    let mut message = "Coding is ".to_string();
    let adjective = "worthwhile";
    message.push_str(&adjective);
    message.push_str(", isn't it?");
    println!("{}", message);
    
    Coding is worthwhile, isn't it?
    

    Key takeaways

    • Concatenation grows a String; a &str literal can't grow.
    • + needs a String on the left and a &str on the right, and it moves the left operand.
    • += and push_str append in place and need a mut variable.
    • format! builds a new string from several values without consuming them.
    Keep going with RustAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free