rust/src/doc/trpl/borrow-and-asref.md
Steve Klabnik e780fb270c TRPL: Borrow and AsRef
These two traits are commonly confused. As such, explain the difference.

Fixes #24163
2015-05-12 10:53:50 -04:00

2.6 KiB
Raw Blame History

% Borrow and AsRef

The Borrow and AsRef traits are very similar, but different. Heres a quick refresher on what these two traits mean.

Borrow

The Borrow trait is used when youre writing a datastructure, and you want to use either an owned or borrowed type as synonymous for some purpose.

For example, HashMap has a get method which uses Borrow:

fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
    where K: Borrow<Q>,
          Q: Hash + Eq

This signature is pretty complicated. The K parameter is what were interested in here. It refers to a parameter of the HashMap itself:

struct HashMap<K, V, S = RandomState> {

The K parameter is the type of key the HashMap uses. So, looking at the signature of get() again, we can use get() when the key implements Borrow<Q>. That way, we can make a HashMap which uses String keys, but use &strs when were searching:

use std::collections::HashMap;

let mut map = HashMap::new();
map.insert("Foo".to_string(), 42);

assert_eq!(map.get("Foo"), Some(&42));

This is because the standard library has impl Borrow<str> for String.

For most types, when you want to take an owned or borrowed type, a &T is enough. But one area where Borrow is effective is when theres more than one kind of borrowed value. Slices are an area where this is especially true: you can have both an &[T] or a &mut [T]. If we wanted to accept both of these types, Borrow is up for it:

use std::borrow::Borrow;
use std::fmt::Display;

fn foo<T: Borrow<i32> + Display>(a: T) {
    println!("a is borrowed: {}", a);
}

let mut i = 5;

foo(&i);
foo(&mut i);

This will print out a is borrowed: 5 twice.

AsRef

The AsRef trait is a conversion trait. Its used for converting some value to a reference in generic code. Like this:

let s = "Hello".to_string();

fn foo<T: AsRef<str>>(s: T) {
    let slice = s.as_ref();
}

Which should I use?

We can see how theyre kind of the same: they both deal with owned and borrowed versions of some type. However, theyre a bit different.

Choose Borrow when you want to abstract over different kinds of borrowing, or when youre building a datastructure that treats owned and borrowed values in equivalent ways, such as hashing and comparison.

Choose AsRef when you want to convert something to a reference directly, and youre writing generic code.