Clone-on-Write
Cow
Use Cases
The type std::borrow::Cow
is a smart pointer providing clone-on-write functionality: it encloses and provides immutable access to borrowed data, and clone the data lazily when mutation or ownership is required.
Cow
optimizes memory usage by delaying cloning until mutation is required, if it is required. It is especially useful in cases where:
- Cloning is costly (long strings, large arrays...),
- Needing to modify the underlying value is rare,
- The stored value is mostly used for read-only purposes.
Cow
is a smart pointer. It implements Deref
, which means that you can call non-mutating methods directly on the data it encloses. If mutation is desired, to_mut
will obtain a mutable reference to an owned value, cloning if necessary.
Accept Either a Owned or Borrowed Value as the Input of a Function
Since Cow
allows borrowing until mutation is needed, it's ideal for functions that take either borrowed or owned strings without unnecessary cloning.
//! Example of a function accepting either owned or borrowed values at its //! input. use std::borrow::Cow; /// A function consuming a `Cow`: fn process_text(input: Cow<str>) { if input.contains("Rust") { println!("Contains Rust!"); } } fn main() { // Call the function with a `&str`, here a string literal. let borrowed_text: Cow<str> = Cow::Borrowed("Hello, Rust!"); process_text(borrowed_text); // Call the function with an owned `String`. let owned_text: Cow<str> = Cow::Owned(String::from("Hello, world!")); process_text(owned_text); // You can also pass a `String` or string slice directly, calling `into()`. let direct_string = String::from("Another example"); process_text(direct_string.into()); }
Modify a Cow
In-place
You can of course pass a &mut Cow<T>
to a function. Modify the underlying value in place with to_mut
:
use std::borrow::Cow; /// Demonstrates modifying a `Cow` in place. /// /// If the input `Cow` contains "Rust", it appends " is awesome!" to it. /// If the `Cow` is borrowed, it will be cloned into an owned `String` before /// modification. fn modify_string(input: &mut Cow<str>) { // Note the mutable reference. if input.contains("Rust") { // `to_mut()` will clone the `Cow` if it's borrowed, // ensuring we have an owned `String` to modify. input.to_mut().push_str(" is awesome!"); } } fn main() { let mut borrowed = Cow::Borrowed("Hello"); modify_string(&mut borrowed); println!("{}", borrowed); let mut borrowed_to_owned = Cow::Borrowed("Rust"); modify_string(&mut borrowed_to_owned); println!("{}", borrowed_to_owned); }
Return a Cow
from a Function
It is common to return a Cow
from a function, if the (borrowed) input is returned unmodified in most, but not all, cases.
//! Example of a function returning a `Cow`. use std::borrow::Cow; /// This function takes a string slice and returns a `Cow<str>`. /// If the input string contains spaces, it returns an owned `String` with /// spaces removed. Otherwise, it returns a borrowed `&str` pointing to the /// original string slice. This avoids unnecessary allocation when no spaces are /// present. fn remove_whitespaces(s: &str) -> Cow<str> { if s.contains(' ') { // Allocate, since we need to modify the string. Cow::Owned(s.to_string().replace(' ', "")) } else { // Nothing to do: just return the string slice. Cow::Borrowed(s) } } fn main() { let original_string = "Hello world! "; let modified_string = remove_whitespaces(original_string); match modified_string { Cow::Borrowed(s) => println!("No spaces, no allocation: {}", s), Cow::Owned(s) => println!("Spaces removed, allocated `String`: {}", s), } let original_string_no_spaces = "HelloWorld!"; let modified_string_no_spaces = remove_whitespaces(original_string_no_spaces); match modified_string_no_spaces { Cow::Borrowed(s) => println!("No spaces, no allocation: {}", s), Cow::Owned(s) => println!("Spaces removed, allocated String: {}", s), } }
Efficiently Construct a Cow
with into
//! Use the `Into` trait to construct `Cow`. //! //! The `Into` trait is the dual of `From` and is implemented for all types that //! implement `From`. Since `impl<'a> From<&'a str> for Cow<'a, str>` and //! `impl<'a> From<String> for Cow<'a, str>` are in the standard library, we can //! simply call `into()` to convert a `&str` or a String into `Cow`. //! //! `Cow` implements `From` for a number of other common types as well: //! (references to) `Vec`, `Path`, `PathBuf`, `OsStr`, `OsString`, etc. use std::borrow::Cow; enum HttpStatus { Ok, NotFound, Custom(u16, String), } /// Call `into` to convert a string slice or a String into a `Cow` with minimum /// fuss. fn describe_status(status: &HttpStatus) -> Cow<'static, str> { // Note the return type. match status { // Convert string literals, of type `&'static str`, into a `Cow`. HttpStatus::Ok => "Status: 200 OK".into(), HttpStatus::NotFound => "Status: 404 Not Found".into(), HttpStatus::Custom(code, message) => { // Convert a `String`, which is dynamically built by `format!`, into // a `Cow`. format!("Status: {} {}", code, message).into() } } } fn main() { let status1 = HttpStatus::Ok; let status2 = HttpStatus::NotFound; let status3 = HttpStatus::Custom(500, "Internal Server Error".to_string()); println!("{}", describe_status(&status1)); // Output: Status: 200 OK println!("{}", describe_status(&status2)); // Output: Status: 404 Not Found println!("{}", describe_status(&status3)); // Output: Status: 500 Internal Server Error }
Convert a Cow
to a borrowed or owned type
To use as a borrowed type, call a method from one of the following traits:
std::borrow::Borrow
⮳,std::convert::AsRef
⮳,std::ops::Deref
⮳ explicitly or implicitly through a coercion.
To convert to an owned type, use std::borrow::Cow::into_owned
⮳, or std::string::ToString
⮳ if a Cow<str>
. Alternatively, use any method to get a reference and then call std::borrow::ToOwned
⮳.
The following example demonstrates how to convert a Cow<str>
to a &str
or a String
:
use std::borrow::Borrow; use std::borrow::Cow; use std::ops::Deref; /// Demonstrates various uses and conversions of `Cow` (Clone-on-Write). fn main() { let mut my_string = String::new(); // Create a `Cow` from the string literal "Example ". let example: Cow<str> = Cow::from("Example "); // Append the `Cow` to a `String` (conversion to a `&str`). my_string.push_str(&example); println!("{}", my_string); // Alternatively, you could use `borrow`, `asref` or `deref`: println!("{}", my_string); my_string.push_str(example.borrow()); println!("{}", my_string); my_string.push_str(example.as_ref()); println!("{}", my_string); my_string.push_str(example.deref()); println!("{}", my_string); // Unstable feature: my_string.push_str(&example.as_str()); // Convert a `Cow<str>` to an owned `String`: let s: String = example.to_string(); println!("{}", s); // You could also convert the `Cow` to a borrowed reference using // `as_ref()`, then clone the borrowed reference to create an owned // `String`. println!("{}", example.as_ref().to_owned()); // `into_owned` extracts the owned data and clones the data if it is not // already owned. println!("{}", example.into_owned()); }
This example was adapted from a StackOverflow discussion⮳.
References
Related Topics
- Lifetimes.
- Memory Management.
- Ownership & Borrowing.
- Rust Patterns.
- Strings.