Good Code
The good version depends on Write, so tests can pass a String, production code can pass another formatter, and errors stay visible.
Lesson 06
Use trait bounds to name the behavior a generic function needs instead of tying code to one concrete type.
use std::fmt::Write;
pub fn append_review_line<W: Write>(writer: &mut W, review: &Review) -> std::fmt::Result {
// The function asks only for the ability to receive formatted text.
writeln!(writer, "{}:{}", review.id, review.title)
}pub fn append_review_line(buffer: &mut String, review: &Review) {
// Concrete String forces callers to allocate even when another writer fits.
buffer.push_str(&format!("{}:{}\n", review.id, review.title));
}The good version depends on Write, so tests can pass a String, production code can pass another formatter, and errors stay visible.
The bad version hard-codes String and uses format! before appending. Callers with an existing writer cannot reuse the function without extra allocation.