String Building
Functional Programming
Tutorial
The Problem
Naive string concatenation with + or repeated format! calls create a new heap allocation on every operation ā O(N²) work for N concatenations. Efficient string building requires pre-allocating capacity and appending in-place. This is the same problem Java's StringBuilder, Python's "".join(list), and C's strbuf solve. Rust's String is essentially a Vec<u8> with UTF-8 invariants, giving direct access to push and capacity management.
🎯 Learning Outcomes
String with push_str (multiple chars) and push (single char)slice::joinString.repeat(n)String::with_capacity to avoid reallocationCode Example
#![allow(clippy::all)]
// 475. String building patterns
#[cfg(test)]
mod tests {
#[test]
fn test_push() {
let mut s = String::new();
s.push_str("hi");
s.push('!');
assert_eq!(s, "hi!");
}
#[test]
fn test_join() {
assert_eq!(vec!["a", "b", "c"].join("-"), "a-b-c");
}
#[test]
fn test_collect() {
let s: String = "abc".chars().rev().collect();
assert_eq!(s, "cba");
}
#[test]
fn test_repeat() {
assert_eq!("ha".repeat(3), "hahaha");
}
#[test]
fn test_capacity() {
let s = String::with_capacity(100);
assert!(s.capacity() >= 100);
}
}Key Differences
Buffer vs. String**: OCaml separates the mutable builder (Buffer.t) from the immutable result (string); Rust's String is mutable throughout its lifetime.Iterator<Item=char> or Iterator<Item=&str> directly into String via the FromIterator trait; OCaml requires String.concat "" (List.map ...).String::with_capacity is explicit and inspectable; OCaml's Buffer.create hint takes an initial capacity hint but it is advisory.repeat**: Rust has str::repeat in the standard library; OCaml needs a manual loop or String.concat "" (List.init n (Fun.const s)).OCaml Approach
OCaml's Buffer module is the StringBuilder equivalent:
let buf = Buffer.create 64
let () = Buffer.add_string buf "hi"
let () = Buffer.add_char buf '!'
let s = Buffer.contents buf (* "hi!" *)
String.concat "-" ["a";"b";"c"] joins with a separator. String.init n f builds a string by calling f i for each index. OCaml lacks a built-in repeat; the idiom is String.concat "" (List.init n (Fun.const s)).
Full Source
#![allow(clippy::all)]
// 475. String building patterns
#[cfg(test)]
mod tests {
#[test]
fn test_push() {
let mut s = String::new();
s.push_str("hi");
s.push('!');
assert_eq!(s, "hi!");
}
#[test]
fn test_join() {
assert_eq!(vec!["a", "b", "c"].join("-"), "a-b-c");
}
#[test]
fn test_collect() {
let s: String = "abc".chars().rev().collect();
assert_eq!(s, "cba");
}
#[test]
fn test_repeat() {
assert_eq!("ha".repeat(3), "hahaha");
}
#[test]
fn test_capacity() {
let s = String::with_capacity(100);
assert!(s.capacity() >= 100);
}
}
✓ Tests
Rust test suite
#[cfg(test)]
mod tests {
#[test]
fn test_push() {
let mut s = String::new();
s.push_str("hi");
s.push('!');
assert_eq!(s, "hi!");
}
#[test]
fn test_join() {
assert_eq!(vec!["a", "b", "c"].join("-"), "a-b-c");
}
#[test]
fn test_collect() {
let s: String = "abc".chars().rev().collect();
assert_eq!(s, "cba");
}
#[test]
fn test_repeat() {
assert_eq!("ha".repeat(3), "hahaha");
}
#[test]
fn test_capacity() {
let s = String::with_capacity(100);
assert!(s.capacity() >= 100);
}
}
Exercises
capitalize_words(s: &str) -> String that capitalises the first letter of each whitespace-delimited word, building the result with push/push_str and no intermediate Vec.fn csv_row(fields: &[&str]) -> String using String::with_capacity to pre-allocate the exact needed capacity (sum of field lengths + separators).String for large documents with frequent mid-string insertions.