ToOwned and Cow (Clone on Write)
Understand Rust's ToOwned trait and the Cow enum to efficiently handle data that is usually borrowed but occasionally needs to be owned, without unnecessary cloning.
The ToOwned trait and the Cow (Clone on Write) enum solve a narrow but common problem: you have a piece of data that is normally read‑only, but some code path requires an owned version. Instead of cloning preemptively every time, you can defer the clone until the moment ownership is actually needed. That is the core of what this pair enables.
The ToOwned Trait
ToOwned is a trait that converts a borrowed reference into an owned value. It lives in std::borrow.
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
}
The most familiar realizations: str implements ToOwned with Owned = String, and [T] implements ToOwned with Owned = Vec<T>. Calling .to_owned() on a &str produces a String; calling it on a &[i32] produces a Vec<i32>.
ToOwned vs Clone on references:
You may have noticed that &str does not implement Clone in the sense of producing a String — clone() on a &str just gives you another &str, a cheap pointer copy. to_owned() is the method that explicitly says “I need the owned version.” That is its entire job.
ToOwned is distinct from Clone in two important ways. First, ToOwned can be implemented for unsized types like str and [T], while Clone requires Sized. Second, to_owned() always returns the owned counterpart; clone() on an already owned type like String returns another String. The two overlap when the type is already owned and also implements Clone — in that case to_owned() typically just delegates to clone().
When a library function needs to accept either borrowed or owned input, ToOwned is usually not the direct tool you reach for — that is where Cow comes in. But understanding ToOwned is necessary because Cow is built on top of it.
The Cow Enum
Cow<'a, B> is an enum defined as:
pub enum Cow<'a, B: ?Sized + 'a>
where
B: ToOwned,
{
Borrowed(&'a B),
Owned(<B as ToOwned>::Owned),
}
It holds either a borrowed reference to some data B or the owned form of that data. The key trick: Cow starts as Borrowed and only becomes Owned when you ask it to mutate. That is the “clone on write” behavior — the clone happens exactly once, at the point of first mutation, and never if no mutation is needed.
Zero-cost read‑only path:
If the code path never needs to modify the data, the Cow remains Borrowed and no allocation ever occurs. This can eliminate entire categories of unnecessary cloning in string processing, path normalization, or configuration handling.
How Cow Decides to Clone
Two methods drive the lazy cloning:
into_owned(self) -> <B as ToOwned>::Owned— consumes theCowand returns an owned value. If it was alreadyOwned, the owned value is returned directly; if it wasBorrowed,to_owned()is called.to_mut(&mut self) -> &mut <B as ToOwned>::Owned— gives a mutable reference to the owned data. If theCowisBorrowed, it first clones the borrowed data into an owned value (making itselfOwned), then returns a mutable reference to that new owned data.
to_mut always triggers a clone on borrowed data:
Calling to_mut() on a Cow::Borrowed will clone the entire data, even if you only intend to read the mutable reference or change a tiny part. The clone is eager in that sense — the moment you ask for mutable access, ownership is sealed. If the data is large, make sure the mutation is genuinely necessary.
You can also check the variant with is_borrowed() and is_owned(), though most real code simply calls into_owned() or to_mut() at the point where a decision is made.
When Cow Shines
Cow is not a general-purpose replacement for String or Vec. It is the right choice when three conditions hold:
- The input is usually borrowed and you only need to read it.
- Some condition triggers a need for an owned, possibly modified version.
- That condition is relatively rare or the input size is large enough that pre‑cloning would be wasteful.
Conditional string transformation
A classic example: a function that lowercases a string only if it is not already lowercase.
use std::borrow::Cow;
fn ensure_lowercase(input: &str) -> Cow<'_, str> {
if input.chars().all(|c| c.is_lowercase()) {
Cow::Borrowed(input)
} else {
Cow::Owned(input.to_lowercase())
}
}
If the string is already lowercase, the caller gets back a borrowed reference with zero allocations. Otherwise a new String is created.
When you run this on a mix of inputs, you can observe which path was taken by checking the variant with matches!:
let result1 = ensure_lowercase("hello"); // stays Borrowed
let result2 = ensure_lowercase("Hello"); // becomes Owned
assert!(matches!(result1, Cow::Borrowed(_)));
assert!(matches!(result2, Cow::Owned(_)));
The mental model: imagine the function first tries the cheap read‑only route. Only when it detects that work is unavoidable does it pay the allocation cost.
API flexibility without forcing callers to clone
APIs that take Cow<str> can accept a &str, a String, or another Cow<str> without the caller having to pre‑wrap anything.
fn log_message<'a>(msg: impl Into<Cow<'a, str>>) {
let msg: Cow<'a, str> = msg.into();
println!("[LOG] {}", msg);
}
// Callers can pass anything:
log_message("static string");
log_message(String::from("owned string"));
log_message(Cow::Borrowed("already a Cow"));
The Into<Cow<str>> blanket implementation for &str and String makes this transparent. The caller never knows or cares whether the function internally clones.
Use Into<Cow<...>> for ergonomic public APIs:
If you are building a library, accepting impl Into<Cow<'_, str>> (or a similar pattern for [T], Path) gives callers maximum flexibility without forcing them to think about ownership. Internally you still get the lazy‑clone benefit.
Configuration with defaults
Cow works nicely for configuration structures where most fields can stay borrowed, but callers occasionally want to supply an owned value.
use std::borrow::Cow;
struct Request<'a> {
host: Cow<'a, str>,
path: Cow<'a, str>,
}
impl<'a> Request<'a> {
fn new() -> Self {
Request {
host: Cow::Borrowed("localhost"),
path: Cow::Borrowed("/"),
}
}
fn with_host(mut self, host: impl Into<Cow<'a, str>>) -> Self {
self.host = host.into();
self
}
}
A Request can be built with borrowed string literals or owned Strings, and the defaults themselves are borrowed and free.
Common Pitfalls
Cow is easy to misuse in ways that erase its benefits or introduce subtle bugs.
Lifetime entanglement
A Cow::Borrowed holds a reference whose lifetime is tied to the original data. If you return a Cow that was created from a short‑lived temporary, you get a compile‑time error — but in more complex scenarios, such as storing Cow inside a struct, the lifetime parameter 'a can become surprisingly restrictive.
struct Parser<'a> {
source: Cow<'a, str>,
}
If you ever want Parser to outlive the original &str, you must move to Cow::Owned. A common beginner mistake is to keep the struct generic over 'a when most instances will end up owning their data anyway, causing lifetimes to leak into every call site. When you know the data will eventually be owned, use Cow<'static, str> and populate it with Cow::Owned(...).
Returning a Cow tied to a local reference:
Returning a Cow::Borrowed(local_ref) from a function where local_ref points to data that goes out of scope will not compile, but returning a Cow::Owned built from that same data is fine. Always check that the borrowed variant genuinely outlives the returned Cow.
Overusing Cow when a simple reference or owned type would do
If a function always needs to own its input, just take String or Vec. If it never needs ownership, take &str or &[T]. Cow adds complexity: a type parameter, lifetime noise, and branching. Use it only when the read‑only fast path genuinely avoids allocations in a measurable or meaningful way.
Silent cloning through to_mut
As noted, to_mut() clones eagerly when the Cow is borrowed. A common mistake is to call to_mut() just to obtain a mutable reference for a tiny edit that could have been avoided by returning a new Cow::Owned only when needed. This mistake is hard to spot because the code still works correctly — it just does unnecessary allocations.
Better: first check whether a change is needed, and if so, create an owned value directly.
// suboptimal: always clones if borrowed, even when no change is needed
fn bad_normalize(s: &mut Cow<str>) {
s.to_mut().make_ascii_lowercase();
}
// better: clone only when a change is actually required
fn good_normalize(input: &str) -> Cow<str> {
if input.bytes().any(|b| b.is_ascii_uppercase()) {
Cow::Owned(input.to_ascii_lowercase())
} else {
Cow::Borrowed(input)
}
}
How ToOwned and Cow Fit Together
The Cow definition requires B: ToOwned. That requirement is what makes the Owned variant possible: <B as ToOwned>::Owned is the concrete owned type. When you write Cow<str>, the compiler knows that str: ToOwned and that Owned = String. So Cow::Owned("...") contains a String.
ToOwned is also the mechanism that powers the lazy clone: Cow::into_owned() calls self.to_owned() on the borrowed data. Without ToOwned, there would be no generic way to convert a &B into an owned form inside Cow.
Cow works for many types beyond str:
Cow<'_, [u8]>, Cow<'_, Path>, and Cow<'_, OsStr> are all valid because those types implement ToOwned. The pattern works anywhere a borrowed‑to‑owned conversion exists.
Choosing Between Clone and ToOwned
When you have a reference and you need an owned value, use to_owned(). When you already have an owned value and you need an independent copy, use clone(). The two are not interchangeable in intent, even if they sometimes do the same thing mechanically.
| Situation | Use |
|---|---|
You have &str and want String | s.to_owned() |
You have String and want another String | s.clone() |
You have &[T] and want Vec<T> | slice.to_owned() |
You have Vec<T> and want another Vec<T> | vec.clone() |
Following this distinction makes the code’s ownership flow obvious to readers. It also prevents accidental clone() on a reference that silently returns another reference when you meant to allocate.
Summary
ToOwned and Cow address a specific tension in Rust: the compiler forces you to decide between borrowed and owned, but many real‑world workflows do not know that answer up front. ToOwned provides the generic conversion from borrowed to owned, and Cow wraps that conversion inside a type that pays for cloning only when ownership becomes inevitable.
The guiding rule is: start borrowed, and become owned only when you must. That rule leads to APIs that accept both &str and String without forcing either, and to functions that skip entire allocation paths when data is already in the desired shape.
The next time you encounter a situation where a function sometimes returns modified data and sometimes returns the input untouched, reach for Cow. If you instead need to accept either borrowed or owned input without lazy cloning, the Into<Cow<...>> pattern gives that flexibility. And when writing a generic data structure that must bridge borrowed and owned forms, ToOwned is the trait that will connect them.