Oliver Jumpertz

Avoiding String Allocations

Category: Rust

Share this snippet to:

use std::borrow::Cow;
fn capitalize_first_letter(word: &str) -> Cow<str> {
match word.chars().nth(0) {
Some(first_letter) if first_letter.is_uppercase() => {
// no allocation necessary
Cow::Borrowed(word)
}
Some(first_letter) => {
// allocation required
let capitalized: String = first_letter.to_uppercase()
.chain(word.chars().skip(1))
.collect();
Cow::Owned(capitalized)
},
// no allocation necessary
None => Cow::Borrowed(word),
}
}
fn main() {
println!("{}", capitalize("oliver")); // Allocation
println!("{}", capitalize("Oliver")); // No allocation
}

Explanation

Cow stands for “clone on write”. It’s a clever type that can work with borrowed, as well as owned, data. When ownership is required, it even allows lazy cloning, so work is done only if and when necessary.

In a case like the above, Cow is a good choice. Capitalizing the first letter of a word is an operation with two possible high-level outcomes: Either the word is already capitalized (or empty), or the word needs to be modified. Only in the latter case, you really need to do an allocation at all because why should you clone a string slice that needs no modification in the first place?

In two of the three cases the function handles, the data can be returned as is, so it only needs to be borrowed and can basically be returned to the sender. If the first letter of the word needs to be capitalized, an allocation is performed by creating a new string and returning it as an owned Cow.

It might not sound like much, but avoiding allocations can sometimes bring a lot of performance. Knowing tricks like this one can greatly improve your quality of life when working with Rust.


Share this snippet to: