:#/ GSLF Journal
← :#/ GSLF Journal

Rustlers Atom 2.8: Cow<T> - Clone On Write

<figure class="video" style="margin:0;">
<div style="position:relative;width:100%;padding-bottom:56.25%;height:0;overflow:hidden;">
<iframe src="https://www.youtube.com/embed/Y1oypTwqTcg?si=aEpMeP2udbmXYqnz"
title="YouTube video player"
style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen></iframe>
</div>
</figure>

Sometimes you want to efficiently return a string slice &str (no allocation) but sometimes you need to modify that string before returning it, which requires a String (heap allocation). Do you return String every time and pay the allocation cost even when you didn't change anything? Do you return Option<String> and make the caller confuse?

Rust provides a standard enum for this exact scenario: std::borrow::Cow (Clone-on-Write). Cow is a smart wrapper that can hold either a borrowed reference or an owned value.

pub enum Cow<'a, B> 
where B: 'a + ToOwned + ?Sized 
{
    Borrowed(&'a B),
    Owned(<B as ToOwned>::Owned),
}

Think of it as a Lazy Clone. It starts as a borrow. It stays a borrow as long as you only read. The moment you try to write to it or call .to_mut(), it clones the data into an owned instance (if it wasn't already owned) and lets you mutate that copy.

In Practice: Cow<str>#

A common use case is string processing where changes are rare.

use std::borrow::Cow;

fn sanitize<'a>(input: &'a str) -> Cow<'a, str> {
    if input.contains("bad_word") {
        // Mutation needed: Allocate a new String
        let replaced = input.replace("bad_word", "****");
        Cow::Owned(replaced)
    } else {
        // No change needed: Just wrap the reference
        Cow::Borrowed(input)
    }
}

fn main() {
    let s1 = "hello world";
    let result1 = sanitize(s1); // Returns Cow::Borrowed
    
    let s2 = "hello bad_word";
    let result2 = sanitize(s2); // Returns Cow::Owned
    
    // Cow implements Deref, so you can treat it like &str
    println!("Result 1: {}", result1); 
    println!("Length: {}", result2.len());
}