Borrow and ToOwned
The Borrow<Borrowed> and ToOwned traits in std::borrow bridge owned types and their unsized borrowed counterparts (such as String and str, or Vec<T> and [T]), enforcing semantic equivalence across hashing, equality, and ordering.
Trait Definitions
pub trait Borrow<Borrowed: ?Sized> {
fn borrow(&self) -> &Borrowed;
}
pub trait ToOwned {
type Owned: Borrow<Self>;
fn to_owned(&self) -> Self::Owned;
fn clone_into(&self, target: &mut Self::Owned) { ... }
}
Every type T implements Borrow<T> (borrowing itself as &T). In addition, owned wrapper types implement Borrow for their unsized view types:
Borrowed Type (?Sized) |
ToOwned::Owned Type |
Relationship |
|---|---|---|
str |
String |
String: Borrow<str> and str: ToOwned<Owned = String> |
[T] (where T: Clone) |
Vec<T> |
Vec<T>: Borrow<[T]> and [T]: ToOwned<Owned = Vec<T>> |
Path |
PathBuf |
PathBuf: Borrow<Path> and Path: ToOwned<Owned = PathBuf> |
CStr |
CString |
CString: Borrow<CStr> and CStr: ToOwned<Owned = CString> |
Borrow<T> vs. AsRef<T>
Although Borrow::borrow(&self) -> &T and AsRef::as_ref(&self) -> &T have identical method signatures, Borrow imposes a strict semantic requirement: Eq, Ord, and Hash on the borrowed value must produce identical results to Eq, Ord, and Hash on the owned value.
Because of this guarantee, HashMap<K, V>::get<Q>(&self, k: &Q) bounds K: Borrow<Q> so a HashMap<String, i32> can be queried using a &str without allocating a String:
use std::borrow::Borrow;
use std::collections::HashMap;
#[derive(Debug, PartialEq, Eq, Hash)]
struct CaseSensitiveKey(String);
impl Borrow<str> for CaseSensitiveKey {
fn borrow(&self) -> &str {
&self.0
}
}
fn main() {
let mut scores = HashMap::new();
scores.insert(CaseSensitiveKey(String::from("team-alpha")), 42);
// Lookup using &str directly without allocating a CaseSensitiveKey:
println!("Lookup result: {:?}", scores.get("team-alpha"));
}
Lookup result: Some(42)
Notice that String implements AsRef<[u8]> but does not implement Borrow<[u8]>, because str and [u8] could differ in hashing details.
ToOwned vs. Clone
Clone::clone(&self) -> Self requires Self: Sized, so calling .clone() on a &str merely copies the fat pointer (&str), returning another &str. By contrast, ToOwned::to_owned(&self) -> Self::Owned works on unsized types (str, [T], Path) and allocates an owned container (String, Vec<T>, PathBuf):
fn main() {
let slice: &[i32] = &[10, 20, 30];
let cloned_ref: &[i32] = slice.clone(); // Copies only the &[i32] reference
let owned_vec: Vec<i32> = slice.to_owned(); // Allocates a Vec<i32>
println!("cloned_ref: {cloned_ref:?}, owned_vec: {owned_vec:?}");
}
cloned_ref: [10, 20, 30], owned_vec: [10, 20, 30]