The book was located under 'src/doc/trpl' because originally, it was going to be hosted under that URL. Late in the game, before 1.0, we decided that /book was a better one, so we changed the output, but not the input. This causes confusion for no good reason. So we'll change the source directory to look like the output directory, like for every other thing in src/doc.
2.6 KiB
% Borrow and AsRef
The Borrow
and AsRef
traits are very similar, but
different. Here’s a quick refresher on what these two traits mean.
Borrow
The Borrow
trait is used when you’re 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 we’re 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 &str
s when we’re 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 there’s more than one
kind of borrowed value. This is especially true of references and slices: 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. It’s 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 they’re kind of the same: they both deal with owned and borrowed versions of some type. However, they’re a bit different.
Choose Borrow
when you want to abstract over different kinds of borrowing, or
when you’re 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
you’re writing generic code.