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
=-=-=-=-=-
push_strappends a&strandpushappends onechar. Both grow one String in place.String::with_capacity(32)reserves room up front, likeVec::with_capacity.join(sep)glues a slice or Vec of strings together with a separator. It's the idiomatic version of the loop above..collect()can build aStringfrom chars (or from&strpieces). Here the chars come out reversed. Name the type (let backwards: String) socollectknows what to build.format!builds a new String from a format string, likeprintln!without printing. Use it whenever you mix text and values.repeat(n)repeats a stringntimes: great for separators and bars.
+ 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