Essential Standard Traits

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]