RustArena

Step 5 of 7

Building strings

There are several ways to put text together. They differ in convenience and in how many allocations they cost.

fn main() {
    let items = ["apple", "bread", "milk"];

    let mut list = String::with_capacity(32);
    for item in items {
        if !list.is_empty() {
            list.push_str(", ");
        }
        list.push_str(item);
    }
    println!("{list}");

    println!("{}", items.join(", "));

    let backwards: String = "stressed".chars().rev().collect();
    println!("{backwards}");

    let line = format!("{} items, first is {}", items.len(), items[0]);
    println!("{line}");
    println!("{}", "=-".repeat(5));
}
apple, bread, milk
apple, bread, milk
desserts
3 items, first is apple
=-=-=-=-=-

+ and ownership

s1 + &s2 works, but it has an ownership twist: + takes s1 by value (reusing its buffer) and borrows the right side, which must be a &str.

fn main() {
    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = s1 + s2;
    println!("{s3}");
}
error[E0308]: mismatched types

The right side must be a reference: s1 + &s2. And afterwards s1 is gone:

fn main() {
    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = s1 + &s2;
    println!("{s1} {s3}");
}
error[E0382]: borrow of moved value: `s1`

For more than two pieces, format!("{s1}-{s2}-{s3}") is clearer and borrows everything. s += "text"; appends in place.

A workplace note on performance

Building a big string with s = s + &piece or a new format! per piece in a loop allocates over and over. Push into one String instead (with with_capacity if you know the size), or collect the pieces into a Vec and join them once. To format directly into an existing String, use std::fmt::Write; and call write!(s, ...).

Your task

Write fn csv_line(fields: &[&str]) -> String that joins fields with commas, following the real CSV rule: a field that contains a comma or a double quote must be wrapped in double quotes, and each double quote inside it doubled. ["Crab, plush", "4"] gives "Crab, plush",4, and say "hi" becomes "say ""hi""".

Challenge 2

Blogs turn titles into URL slugs. Write fn slugify(title: &str) -> String: lowercase the title, keep letters and digits (is_alphanumeric()), and turn every run of other characters into a single -, with no - at the start or end. "Hello, World!" gives "hello-world".

Challenge 3

Each input line is item qty. Print one summary line joining the entries with +, then the total quantity:

3 x apple + 1 x bread + 12 x egg
total items: 16

Build the pieces with format! into a Vec<String>, then join them.

Previous: Trimming and searching Next: Formatting: width, alignment, precision