The Secret Life of Cows

 — 2 minutes read


Pascal Hertleif gives a comprehensive introduction to the Cow type and discusses when and how to use it:

One very cool use-case for Cows is when dealing with functions that either return static strings (i.e., strings you literally write in your source code) or dynamic strings that get put together at run time. The Programming Rust book by Jim Blandy and Jason Orendorff contains an example like this:

use std::borrow::Cow;

fn describe(error: &Error) -> Cow<'static, str> {
    match *error {
        Error::NotFound => "Error: Not found".into(),
        Error::Custom(e) => format!("Error: {}", e).into(),
    }
}

Why is this a very cool example? Reddit user 0x7CFE put it like this:

The most important thing is that since Cow<‘static, str> derefs to &str it may act as a drop-in replacement everywhere, where &str is expected. So, if last error variant was added to an already existing code base, all would just work without any major refactoring.

[…]

Rust provides a solution that is zero cost for cases where extra details are not needed. It’s a brilliant example of “pay only for what you use” principle in action.

So basically when you need to return an owned or a referenced string, but it’s not clear which exactly it will be, Cow gives you a way to express this. And of course this holds for any type not just strings.

And together with AsRef you can write functions that take both owned types and references and return a Cow. Dereferencing of the input and allocation for the output is then handles accordingly1:

use std::borrow::Cow;

fn pay_per_use<'a, T: AsRef<str>>(a_string: T) -> Cow<'a, str> {
    a_string.as_ref().to_lowercase().into()
}

fn main() {
    let a_str = "Lukas was here";
    let a_string = "Lukas was here".to_owned();

    println!("{}", pay_per_use(a_str));
    println!("{}", pay_per_use(&a_string));
    println!("{}", pay_per_use(a_string));
}

  1. See this playground.