Borrowed Data Structures
A borrowed data structure (also called a zero-copy view type) is a struct or enum parameterized by one or more lifetimes (such as Packet<'a> or AstNode<'src>) that stores slices (&'a str, &'a [u8]) pointing directly into an external backing buffer instead of allocating owned String or Vec<u8> copies on the heap.
Zero-Copy Parsing Architecture
When parsing structured text (JSON, HTTP headers, configuration files, source code) or binary network frames, allocating a separate String for every token or header field can dominate execution time. A borrowed data structure slices the input buffer in place:
Caller's Owned Backing Buffer (`raw_request: String` on Heap):
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| G | E | T | | / | a | p | i | / | v | 1 | | H | T | T | P | 1 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
^ ^ ^ ^ ^ ^
| | | | | |
+-------+ +-----------------------+ +---------------+
method: &'a str path: &'a str version: &'a str
(ptr, len = 3) (ptr, len = 7) (ptr, len = 5)
#[derive(Debug, PartialEq, Eq)]
struct HttpRequestLine<'buf> {
method: &'buf str,
path: &'buf str,
version: &'buf str,
}
impl<'buf> HttpRequestLine<'buf> {
fn parse(line: &'buf str) -> Option<Self> {
let mut parts = line.split_whitespace();
let method = parts.next()?;
let path = parts.next()?;
let version = parts.next()?;
Some(Self {
method,
path,
version,
})
}
}
fn main() {
let raw_buffer = String::from("GET /api/v1/items HTTP/1.1\r\n");
let parsed = HttpRequestLine::parse(&raw_buffer).unwrap();
println!("parsed zero-copy view: {:?}", parsed);
}
parsed zero-copy view: HttpRequestLine { method: "GET", path: "/api/v1/items", version: "HTTP/1.1" }
While parsed is alive, the borrow checker prevents raw_buffer from being mutated or dropped, guaranteeing that parsed.method, parsed.path, and parsed.version never dangle.
Hybrid Borrowed-or-Owned Structures with Cow<'a, str>
Purely borrowed slices (&'a str) work when the substring appears verbatim in the input buffer. If a string occasionally requires unescaping (for example, replacing \" with " in JSON or decoding %20 in a URL), using std::borrow::Cow<'a, str> borrows verbatim substrings with zero allocation (Cow::Borrowed(&'a str)) and allocates a String (Cow::Owned(String)) only when an escape sequence is present:
use std::borrow::Cow;
#[derive(Debug)]
struct QueryParam<'a> {
key: &'a str,
value: Cow<'a, str>,
}
fn parse_param(pair: &str) -> Option<QueryParam<'_>> {
let (key, raw_val) = pair.split_once('=')?;
let value = if raw_val.contains('+') {
Cow::Owned(raw_val.replace('+', " "))
} else {
Cow::Borrowed(raw_val)
};
Some(QueryParam { key, value })
}
fn main() {
let p1 = parse_param("lang=rust").unwrap();
let p2 = parse_param("title=advanced+lifetimes").unwrap();
println!(
"p1: {:?} (borrowed = {})",
p1,
matches!(p1.value, Cow::Borrowed(_))
);
println!(
"p2: {:?} (borrowed = {})",
p2,
matches!(p2.value, Cow::Borrowed(_))
);
}
p1: QueryParam { key: "lang", value: "rust" } (borrowed = true)
p2: QueryParam { key: "title", value: "advanced lifetimes" } (borrowed = false)
Detaching a Borrowed Structure via .into_owned()
By providing an into_owned(self) -> HttpRequestLine<'static> method on a Cow-based structure, callers can parse at zero-copy speed during request handling and convert only the specific structs that need to be stored long-term into 'static owned instances.
| Design Approach | Allocation Cost | Ergonomics Across Threads / Async |
|---|---|---|
Owned (String, Vec<u8>) |
1 heap allocation per field | High ('static, easy to store and send) |
Purely Borrowed (&'a str, &'a [u8]) |
0 allocations |
Tied to backing buffer lifetime 'a |
Hybrid (Cow<'a, str>, Cow<'a, [u8]>) |
0 on fast path; allocates only on mutation/unescape |
Can convert to 'static via Cow::into_owned |
Shared Slice (Arc<str>, Bytes) |
Reference-count increment only | High ('static, no lifetime parameters) |