Loading page…
Rust walkthroughs
Loading page…
Cow<'a, B> (Clone on Write) is a smart pointer type that can hold either borrowed data (&B) or owned data (B). It allows you to defer cloning until modification is needed, optimizing for cases where data is often used read-only.
Key concepts:
&'a B (borrowed) or B (owned)ToOwned trait — Types that can be converted from borrowed to ownedBorrow trait — Types that can be borrowed from ownedCommon use cases:
When Cow shines:
use std::borrow::Cow;
fn main() {
// Cow can hold borrowed data
let borrowed: Cow<str> = Cow::Borrowed("hello");
println!("Borrowed: {}", borrowed);
// Cow can hold owned data
let owned: Cow<str> = Cow::Owned(String::from("hello world"));
println!("Owned: {}", owned);
// Both can be used the same way
fn print_cow(s: Cow<str>) {
println!("Value: {}", s);
}
print_cow(borrowed);
print_cow(owned);
}use std::borrow::Cow;
fn add_prefix<'a>(input: Cow<'a, str>) -> Cow<'a, str> {
if input.starts_with("prefix_") {
// No modification needed, return as-is
input
} else {
// Need to modify, so we clone and modify
let mut modified = input.into_owned();
modified.insert_str(0, "prefix_");
Cow::Owned(modified)
}
}
fn main() {
// Already has prefix - no allocation
let s1 = Cow::Borrowed("prefix_hello");
let result1 = add_prefix(s1);
println!("Result 1: {}", result1);
// Needs prefix - allocates
let s2 = Cow::Borrowed("world");
let result2 = add_prefix(s2);
println!("Result 2: {}", result2);
}use std::borrow::Cow;
fn process_string<'a>(input: &'a str) -> Cow<'a, str> {
// If the string is already lowercase, return borrowed
// Otherwise, create an owned lowercase version
if input.chars().all(|c| !c.is_ascii_uppercase()) {
Cow::Borrowed(input)
} else {
Cow::Owned(input.to_lowercase())
}
}
fn main() {
// No allocation needed
let result1 = process_string("hello world");
println!("Result 1: {}", result1);
// Allocation needed
let result2 = process_string("Hello World");
println!("Result 2: {}", result2);
}use std::borrow::Cow;
// Accept both &str and String without forcing ownership
fn greet<'a>(name: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
let name = name.into();
if name.is_empty() {
Cow::Borrowed("Anonymous")
} else {
name
}
}
fn main() {
// Pass &str
let result1 = greet("Alice");
println!("{}", result1);
// Pass String
let result2 = greet(String::from("Bob"));
println!("{}", result2);
// Pass empty
let result3 = greet("");
println!("{}", result3);
}use std::borrow::Cow;
use std::path::{Path, PathBuf};
fn canonicalize_path<'a>(path: &'a Path) -> Cow<'a, Path> {
// If path is already absolute, return as-is
if path.is_absolute() {
Cow::Borrowed(path)
} else {
// Otherwise, create owned absolute path
let mut absolute = std::env::current_dir().unwrap();
absolute.push(path);
Cow::Owned(absolute)
}
}
fn main() {
// Relative path - needs allocation
let relative = std::path::Path::new("src/main.rs");
let result1 = canonicalize_path(relative);
println!("Result 1: {:?}", result1);
// Absolute path - no allocation
let absolute = std::path::Path::new("/usr/bin/rustc");
let result2 = canonicalize_path(absolute);
println!("Result 2: {:?}", result2);
}use std::borrow::Cow;
fn add_element<'a>(input: Cow<'a, [i32]>, element: i32) -> Cow<'a, [i32]> {
// Check if element already exists
if input.contains(&element) {
return input;
}
// Need to add element - clone and modify
let mut result = input.into_owned();
result.push(element);
Cow::Owned(result)
}
fn main() {
let data = vec![1, 2, 3, 4, 5];
// Element exists - no allocation
let result1 = add_element(Cow::Borrowed(&data), 3);
println!("Result 1: {:?}", result1);
// Element doesn't exist - allocates
let result2 = add_element(Cow::Borrowed(&data), 6);
println!("Result 2: {:?}", result2);
}use std::borrow::Cow;
fn main() {
let borrowed: Cow<str> = Cow::Borrowed("hello");
// to_mut() returns &mut only if we need to modify
// It clones on first mutation
let mut cow = Cow::Borrowed("hello");
// First call to to_mut() - no clone needed yet
// Just reading
if cow.starts_with("he") {
println!("Starts with 'he'");
}
// Need to modify - to_mut() will clone here
cow.to_mut().push_str(" world");
println!("Modified: {}", cow);
// Now cow is Owned
match cow {
Cow::Owned(ref s) => println!("Is owned: {}", s),
Cow::Borrowed(s) => println!("Is borrowed: {}", s),
}
}use std::borrow::Cow;
fn main() {
let borrowed: Cow<str> = Cow::Borrowed("hello");
// into_owned() always produces owned data
// Clones if currently borrowed
let owned: String = borrowed.into_owned();
println!("Owned: {}", owned);
// If already owned, just returns the inner value
let already_owned: Cow<str> = Cow::Owned(String::from("world"));
let result: String = already_owned.into_owned();
println!("Result: {}", result);
}use std::borrow::Cow;
fn trim<'a>(input: Cow<'a, str>) -> Cow<'a, str> {
let trimmed = input.trim();
if trimmed.len() == input.len() {
input
} else {
Cow::Owned(trimmed.to_string())
}
}
fn uppercase<'a>(input: Cow<'a, str>) -> Cow<'a, str> {
if input.chars().all(|c| !c.is_ascii_lowercase()) {
input
} else {
Cow::Owned(input.to_uppercase())
}
}
fn add_suffix<'a>(input: Cow<'a, str>, suffix: &str) -> Cow<'a, str> {
if input.ends_with(suffix) {
input
} else {
let mut result = input.into_owned();
result.push_str(suffix);
Cow::Owned(result)
}
}
fn process<'a>(input: &'a str) -> Cow<'a, str> {
let result = Cow::Borrowed(input);
let result = trim(result);
let result = uppercase(result);
let result = add_suffix(result, "!");
result
}
fn main() {
// No modifications needed
let result1 = process("HELLO!");
println!("Result 1: {}", result1);
// Needs modifications
let result2 = process(" hello ");
println!("Result 2: {}", result2);
}use std::borrow::Cow;
fn get_message(code: u32) -> Cow<'static, str> {
match code {
0 => Cow::Borrowed("Success"),
1 => Cow::Borrowed("Invalid input"),
2 => Cow::Borrowed("Not found"),
// Dynamic error message
n => Cow::Owned(format!("Unknown error code: {}", n)),
}
}
fn main() {
println!("Error 0: {}", get_message(0));
println!("Error 1: {}", get_message(1));
println!("Error 42: {}", get_message(42));
}use std::borrow::Cow;
#[derive(Debug, Clone)]
struct Config {
name: String,
value: i32,
}
impl std::borrow::Borrow<str> for Config {
fn borrow(&self) -> &str {
&self.name
}
}
impl ToOwned for Config {
type Owned = Config;
fn to_owned(&self) -> Config {
self.clone()
}
}
fn get_config<'a>(name: &str, configs: &'a [Config]) -> Cow<'a, Config> {
// Find config by name
if let Some(config) = configs.iter().find(|c| c.name == name) {
return Cow::Borrowed(config);
}
// Return default config (owned)
Cow::Owned(Config {
name: name.to_string(),
value: 0,
})
}
fn main() {
let configs = vec![
Config { name: "timeout".to_string(), value: 30 },
Config { name: "retries".to_string(), value: 3 },
];
// Found - returns borrowed
let result1 = get_config("timeout", &configs);
println!("Found: {:?}", result1);
// Not found - returns owned default
let result2 = get_config("max_size", &configs);
println!("Default: {:?}", result2);
}use std::borrow::Cow;
struct Settings {
defaults: Vec<(String, String)>,
}
impl Settings {
fn new() -> Self {
Self {
defaults: vec![
("host".to_string(), "localhost".to_string()),
("port".to_string(), "8080".to_string()),
("timeout".to_string(), "30".to_string()),
],
}
}
fn get<'a>(&'a self, key: &str, user_value: Option<&'a str>) -> Cow<'a, str> {
match user_value {
Some(value) => Cow::Borrowed(value),
None => {
// Look up default
self.defaults
.iter()
.find(|(k, _)| k == key)
.map(|(_, v)| Cow::Borrowed(v.as_str()))
.unwrap_or_else(|| Cow::Owned(String::new()))
}
}
}
}
fn main() {
let settings = Settings::new();
// User-provided value
let result1 = settings.get("host", Some("example.com"));
println!("Host: {}", result1);
// Default value
let result2 = settings.get("port", None);
println!("Port: {}", result2);
}use std::borrow::Cow;
struct Message<'a> {
title: Cow<'a, str>,
body: Cow<'a, str>,
}
impl<'a> Message<'a> {
fn new(title: impl Into<Cow<'a, str>>, body: impl Into<Cow<'a, str>>) -> Self {
Self {
title: title.into(),
body: body.into(),
}
}
fn with_static_title(title: &'static str) -> Self {
Self {
title: Cow::Borrowed(title),
body: Cow::Owned(String::new()),
}
}
}
fn main() {
// Static title, dynamic body
let msg1 = Message::new("Alert", String::from("Something happened!"));
println!("Title: {}, Body: {}", msg1.title, msg1.body);
// Both borrowed
let msg2 = Message::new("Warning", "Check this out");
println!("Title: {}, Body: {}", msg2.title, msg2.body);
}use std::borrow::Cow;
fn process_bytes<'a>(input: &'a [u8]) -> Cow<'a, [u8]> {
// Check if all bytes are ASCII
if input.iter().all(|b| b.is_ascii()) {
// Check if all lowercase
if input.iter().all(|b| !b.is_ascii_uppercase()) {
return Cow::Borrowed(input);
}
}
// Need to process - create owned
let processed: Vec<u8> = input
.iter()
.map(|b| b.to_ascii_lowercase())
.collect();
Cow::Owned(processed)
}
fn main() {
// Already lowercase ASCII - no allocation
let data1 = b"hello world";
let result1 = process_bytes(data1);
println!("Result 1: {:?}", result1);
// Needs processing - allocates
let data2 = b"Hello World";
let result2 = process_bytes(data2);
println!("Result 2: {:?}", result2);
}use std::borrow::Cow;
fn describe_cow(cow: &Cow<str>) -> &'static str {
match cow {
Cow::Borrowed(s) => {
println!("Borrowed: {}", s);
"borrowed"
}
Cow::Owned(s) => {
println!("Owned: {}", s);
"owned"
}
}
}
fn main() {
let borrowed: Cow<str> = Cow::Borrowed("hello");
let owned: Cow<str> = Cow::Owned(String::from("world"));
println!("First is: {}", describe_cow(&borrowed));
println!("Second is: {}", describe_cow(&owned));
}Cow<'a, B> Variants:
| Variant | Holds | When to Use |
|---------|-------|-------------|
| Borrowed(&'a B) | Reference | Data already exists, no modification needed |
| Owned(B) | Owned value | Data was modified or created |
Key Methods:
| Method | Description |
|--------|-------------|
| Borrowed(val) | Create borrowed Cow |
| Owned(val) | Create owned Cow |
| into_owned() | Get owned value (clones if needed) |
| to_mut() | Get mutable reference (clones if borrowed) |
| is_borrowed() | Check if borrowed |
| is_owned() | Check if owned |
Common Type Parameters:
| Cow Type | Borrowed | Owned |
|----------|----------|-------|
| Cow<'a, str> | &'a str | String |
| Cow<'a, [T]> | &'a [T] | Vec<T> |
| Cow<'a, Path> | &'a Path | PathBuf |
| Cow<'a, CStr> | &'a CStr | CString |
| Cow<'a, OsStr> | &'a OsStr | OsString |
When to Use Cow:
| Scenario | Appropriate? | |----------|-------------| | Read-mostly data | ✅ Yes | | Avoiding unnecessary clones | ✅ Yes | | Returning static or dynamic strings | ✅ Yes | | Processing pipelines | ✅ Yes | | Always modifying data | ❌ Just use owned | | Short-lived operations | ❌ Just clone |
Key Points:
&T) or owned (T) datato_mut() clones only when first mutation is neededinto_owned() returns owned, cloning if necessaryToOwned traitstr, [T], Path, OsStr, CStr'a allows returning borrowed data from functionCow<'static, str> can hold static strings without allocation