Rust Cow: Borrow or Build
Cow is a text value that borrows the original text or owns a changed copy. Cow is short for clone on write.
Returning String copies every time
A function that returns String must build a new String for every input, even the inputs that change nothing:
fn main() {
let inputs = ["tidy", " tidy ", "also tidy"];
let mut copies = 0;
for text in inputs {
let cleaned: String = text.trim().to_string();
copies += 1;
println!("{}", cleaned);
}
println!("{} copies for {} inputs", copies, inputs.len());
}Two variants, one type
A Cow holds either the borrowed input, untouched, or an owned String built because a change was needed:
let borrowed: Cow<str> = Cow::Borrowed("already tidy");
let owned: Cow<str> = Cow::Owned(String::from("built fresh"));Borrow when nothing changes
exclaim only builds a new String for inputs that lack their !:
use std::borrow::Cow;
fn exclaim(input: &str) -> Cow<'_, str> {
if input.ends_with('!') {
Cow::Borrowed(input)
} else {
Cow::Owned(format!("{input}!"))
}
}Ask which one you got
matches! tells the variants apart, when it matters:
let a = exclaim("hello");
let b = exclaim("wow!");
println!("a = {} (borrowed: {})", a, matches!(a, Cow::Borrowed(_)));
println!("b = {} (borrowed: {})", b, matches!(b, Cow::Borrowed(_)));into_owned pays only when borrowed
At the point a real String is required, into_owned settles the bill — for the borrowed input only:
let owned: String = exclaim("wow!").into_owned();
println!("owned = {}", owned);to_mut copies at the first write
Reading a Cow never copies. Writing through to_mut copies at exactly that line:
let mut c = exclaim("hey!");
println!("before write, borrowed = {}", matches!(c, Cow::Borrowed(_)));
c.to_mut().push('!');
println!("after write = {} (borrowed: {})", c, matches!(c, Cow::Borrowed(_)));Example
use std::borrow::Cow;
fn exclaim(input: &str) -> Cow<'_, str> {
if input.ends_with('!') {
Cow::Borrowed(input)
} else {
Cow::Owned(format!("{input}!"))
}
}
fn main() {
let a = exclaim("hello");
let b = exclaim("wow!");
println!("a = {} (borrowed: {})", a, matches!(a, Cow::Borrowed(_)));
println!("b = {} (borrowed: {})", b, matches!(b, Cow::Borrowed(_)));
let owned: String = b.into_owned();
println!("owned = {}", owned);
let mut c = exclaim("hey!");
println!("before write, borrowed = {}", matches!(c, Cow::Borrowed(_)));
c.to_mut().push('!');
println!("after write = {} (borrowed: {})", c, matches!(c, Cow::Borrowed(_)));
let d = exclaim("rust");
println!("len = {} upper = {}", d.len(), d.to_uppercase());
}Key points
- Some functions usually return their input unchanged.
- Return Cow from those functions.
- Clean text is one case.
- An existing suffix is another case.
- Already-lowercase text is a third case.
- Borrow those cases, and own only the exceptions.
- A Cow appeared earlier: String::from_utf8_lossy returns one.
- It is Borrowed when the bytes were clean, and then it allocated nothing.
- It is Owned when the bytes were not clean, and then it holds � repairs.
In this example, exclaim lends or builds depending on its input. "hello" has no !, so the answer is Owned("hello!") — borrowed: false. "wow!" is already fine, so the answer is Borrowed, the very input — borrowed: true, and no copy was made. into_owned turns that borrowed Cow into a String, paying the one copy only now. A second Cow starts borrowed too, and to_mut().push('!') forces its copy at the write, flipping borrowed to false and giving hey!!. The last line never asks which variant it holds. len works on a Cow. to_uppercase works, too. They work exactly as on a &str. A Cow derefs to str.
Note
Cow is not a text-only trick.
Cow<[T]> wraps slices the same way.
Text is the common case: the borrow-or-build shape earns its keep most often there.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
A function returns its input unchanged 95% of the time and a fixed-up version 5% of the time. Which return type fits?