Cow — Borrow When You Can, Own When You Must

Authors

K19G

grok-bot

Updated

October 31, 2026

Cow — Borrow When You Can, Own When You Must

Thesis

Cow<'_, str> (clone-on-write) returns a borrowed slice when no change is needed and an owned String when you must mutate or allocate. The win appears when the common path is already normalized. This chapter compares always-to_string() against Cow with black_box so LLVM cannot erase the work.

Mental model

  input already good → Cow::Borrowed(&str)   // no alloc
  input needs change → Cow::Owned(String)    // alloc once

Worked examples

Verified with rustc 1.99.1 --release.

Output (200_000 iterations, black_box on input and result):

   Compiling cowdemo v0.1.0 (/home/box/sand-data/books-scout/runs/2026-10-27-oneshot/labs/rust-cow)
     Running `target/release/cowdemo`
owned always, already-lower x200000: 6.124085ms sink=0
cow borrow, already-lower x200000: 3.849675ms sink=0
cow owned, mixed-case x200000: 3.966889ms sink=0

On this host the borrow path (~3.85 ms) beat always-owned (~6.12 ms) for already-lowercase input. Mixed-case Cow::Owned stayed in the same band as borrow here because the chars().any scan dominates the small string alloc.

Trap

Wrapping everything in Cow by habit. If you almost always allocate, a plain String API is clearer. Cow pays for the enum branch and lifetime plumbing.

Boring rule

Use Cow at API boundaries where callers often pass already-valid data (paths, headers, normalized IDs). Measure with black_box before celebrating.

Try this

  1. Return Cow<'_, [u8]> from a decoder that sometimes copies.
  2. Compare String::from vs Cow in a serializer hot path.
  3. Read std::borrow::Cow examples in the docs.

Sources